home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / IFC_112 / netscape / application / TextView.java < prev    next >
Encoding:
Text File  |  1999-05-28  |  146.9 KB  |  4,343 lines  |  [TEXT/CWIE]

  1. // TextView.java
  2. // By Ned Etcode
  3. // Copyright 1995, 1996, 1997 Netscape Communications Corp.  All rights reserved.
  4.  
  5. package netscape.application;
  6.  
  7. import netscape.util.*;
  8. import java.io.*;
  9. import java.net.*;
  10.  
  11. /* drawtext variable needs mutex protection */
  12.  
  13. /** View subclass that displays zero or more paragraphs of multi-font text
  14.   * or embedded Images.  TextView stores the displayed text as a collection
  15.   * of Strings and a Hashtable that describes the attributes associated with
  16.   * each String.  Each Hashtable applies to a character range. A character
  17.   * range containing the same attributes is called a "run." Paragraphs
  18.   * are defined as collections of runs ending with a carriage return.<p>
  19.   * Any attribute can be associated with a character range, however
  20.   * TextView defines some special attributes that are interpreted by the
  21.   * layout engine when formatting and displaying the text.  When one or more
  22.   * characters are inserted into the TextView by typing or by using the
  23.   * insertion methods, the following rules apply to determine how attributes
  24.   * apply to the new ranges:<OL>
  25.   * <LI>If there is a run after the inserted range, the inserted characters'
  26.   * attributes will be <b>defaultAttributes()</b> UNION the following run's
  27.   * attributes.
  28.   * <LI>If a run appears before the inserted range, the inserted characters'
  29.   * attributes will be <b>defaultAttributes()</b> UNION the previous run's
  30.   * attributes.
  31.   * <LI>If the previous two conditions don't apply, the inserted characters'
  32.   * attributes will be <b>defaultAttributes()</b>.
  33.   * </OL>
  34.   * Finally, if some typingAttributes have been set, typing attributes are
  35.   * applied to what have been typed. Typing attributes are used only once,
  36.   * after the normal attributes propagation mechanism applies.
  37.   * @note 1.0 It is now possible to use views as TextAttachments.
  38.   *           TextAttachment provides some new notifications so the
  39.   *           attachment subclass can know when to add a view to view
  40.   *           hierarchy, update it's bounds and remove it from the view
  41.   *           hierarchy.
  42.   * @note 1.0 It is now possible to extend HTML parsing so TextView supports
  43.   *           new tags when parsing HTML.
  44.   * @note 1.0 Some performance improvements.
  45.   * @note 1.0 Added FormElement interface for browser needs
  46.   * @note 1.0 filterEvents bug fixed.
  47.   * @note 1.1 Paragraphs will be reformated after inserting a string
  48.   * @note 1.1 Caps Lock will not result in a keystroke
  49.   * @note 1.1 MouseEvents under 1.1.x should work slightly more reliably
  50.   * @note 1.1.1 Now supports extended key events if available
  51.   * @note 1.1.1 setBaseURL is now public.
  52.   */
  53. public class TextView extends View implements ExtendedTarget, EventFilter, DragDestination, FormElement {
  54.  
  55.     /** String used to store a TextAttachment instance in the text.
  56.      * @see TextAttachment
  57.      */
  58.     public final static String TEXT_ATTACHMENT_STRING = "@";
  59.  
  60.     /** ParagraphFormat attribute. Value is a ParagraphFormat instance. */
  61.     public final static String PARAGRAPH_FORMAT_KEY = "ParagraphFormatKey";
  62.  
  63.     /** Font attribute.  Value is a Font instance. */
  64.     public final static String FONT_KEY = "FontKey";
  65.  
  66.     /** TextColor attribute. Value is a Color instance. */
  67.     public final static String TEXT_COLOR_KEY = "TextColorKey";
  68.  
  69.     /** TextAttachment attribute. A TextAttachment is an attachment, and this
  70.       * attribute is generally set on a single character. The character will be
  71.       * replaced by the TextAttachment
  72.       * This attributes is static. It is not propagated while typing and
  73.       * cannot be used as a typing attribute.
  74.       */
  75.     public final static String TEXT_ATTACHMENT_KEY = "TextAttachmentKey";
  76.  
  77.     /** TextAttachment baseline offset attribute. The value of this attribute
  78.       * is the distance between the bottom of the attachment and the text
  79.       * baseline. This attribute makes sense only when used with
  80.       * <B>TEXT_ATTACHMENT_KEY</B>.  The distance is stored as an
  81.       * <B>Integer</B> instance and is a number of pixels.
  82.       *  The default value of this attribute is 0.
  83.       * This attributes is static. It is not propagated while typing and
  84.       * cannot be used as a typing attribute.
  85.       */
  86.     public final static String TEXT_ATTACHMENT_BASELINE_OFFSET_KEY =
  87.                                         "TextAttachmentBaselineOffsetKey";
  88.  
  89.  
  90.     /** Caret Color attribute. */
  91.     public final static String CARET_COLOR_KEY = "CaretColorKey";
  92.  
  93.     /** "Link" attribute. A range that has this attribute will tell the
  94.       * <b>TextViewOwner</b>
  95.       * to follow the link when the users clicks the region. The value is a
  96.       * string, containing an URL.
  97.       * This attributes is static. It is not propagated while typing and
  98.       * cannot be used as a typing attribute.
  99.      */
  100.     public final static String LINK_KEY = "LinkKey";
  101.  
  102.     /** Local link destination attribute. A range that has this attribute
  103.       * will be marked as the destination a link. The value is the link's
  104.       * name.
  105.       * This attributes is static. It is not propagated while typing and
  106.       * cannot be used as a typing attribute.
  107.       */
  108.     public final static String LINK_DESTINATION_KEY = "LinkDestinationKey";
  109.  
  110.     /** Link color attribute. Usually, you use this attribute
  111.       * only on the default attribute. You can however use it on a single link.
  112.       */
  113.     public final static String LINK_COLOR_KEY = "LinkColorKey";
  114.  
  115.     /** Link pressed attribute. */
  116.     public final static String PRESSED_LINK_COLOR_KEY = "PressedLinkColorKey";
  117.  
  118.     /** Flag to indicate that the link has been pressed
  119.       * @private
  120.       */
  121.     final static String LINK_IS_PRESSED_KEY = "_IFCLinkPressedKey";
  122.  
  123.     /** Vector of attributes changing text formatting when changed */
  124.     static Vector attributesChangingFormatting;
  125.  
  126.     static {
  127.         attributesChangingFormatting = new Vector();
  128.         attributesChangingFormatting.addElement(TEXT_ATTACHMENT_KEY);
  129.         attributesChangingFormatting.addElement(TEXT_ATTACHMENT_BASELINE_OFFSET_KEY);
  130.         attributesChangingFormatting.addElement(FONT_KEY);
  131.         attributesChangingFormatting.addElement(PARAGRAPH_FORMAT_KEY);
  132.     }
  133.  
  134.     Vector              _paragraphVector;
  135.     Color               _backgroundColor, _selectionColor;
  136.     TextParagraph       _updateParagraph;
  137.     TextPositionInfo    _anchorInfo, _upInfo;
  138.     TextSelection       _selection;
  139.     TextFilter          _filter;
  140.     TextViewOwner       _owner;
  141.     Hashtable           _defaultAttributes;
  142.     Hashtable           _typingAttributes;
  143.     Timer               _updateTimer;
  144.     Vector              _eventVector;
  145.     int                 _charCount, _paragraphSpacing, _updateLine, _downY,
  146.                         _clickCount, _resizeDisabled,_formattingDisabled;
  147.     boolean             _drawText = true, _editing,
  148.                         _useSingleFont = false, _editable = true,
  149.                         _selectable = true, _drawNextParagraph,
  150.                         _resizing = false, insertionPointVisible = false,
  151.                         transparent = false,selectLineBreak;
  152.  
  153.     private Range       _selectedRange; /* This is a cache and is not always up to date. use selectedRange()
  154.                                            the selected range */
  155.     private Range       _wasSelectedRange;
  156.  
  157.     private TextAttachment    _mouseDownTextAttachment;   /* Used only during a mouseDown->mouseDrag->mouseUp session
  158.                                                              the text item under the mouse */
  159.     private Point       _mouseDownTextAttachmentOrigin; /* the text item origin in the textview */
  160.     private FontMetrics _defaultFontMetricsCache; /* Cache default font font metrics */
  161.  
  162.     private URL         _baseURL;                 /* Base URL. Used only while parsing HTML */
  163.     private Range       _clickedRange;            /* Range of the clicked link */
  164.     private Range       _firstRange;              /* Use while selecting per words/line/paragraph*/
  165.  
  166.     private HTMLParsingRules  _htmlParsingRules;              /* HTML parsing rules */
  167.     private int notifyAttachmentDisabled = 0;       /* If > 0 do not notify attachment. Store changed range into: */
  168.     private Range invalidAttachmentRange = null;
  169.  
  170.     static private Vector    _rectCache = new Vector();
  171.     static private Vector    _vectorCache = new Vector();
  172.     static private boolean      _shouldCache = false, _cacheVectors = false;
  173.     static         ObjectPool hashtablePool = new ObjectPool("netscape.util.Hashtable",32);
  174.     static         ObjectPool rangePool     = new ObjectPool("netscape.application.Range",32);
  175.  
  176.     static final String         PARAGRAPHVECTOR_KEY = "paragraphVector";
  177.     static final String         BACKGROUNDCOLOR_KEY = "backgroundColor";
  178.     static final String         SELECTIONCOLOR_KEY = "selectionColor";
  179.     static final String         FILTER_KEY = "filter";
  180.     static final String         DEFAULTATTRIBUTES_KEY = "defaultAttributes";
  181.     static final String         PARASPACING_KEY = "paragraphSpacing";
  182.     static final String         USESINGLEFONT_KEY = "useSingleFont";
  183.     static final String         EDITABLE_KEY = "editable";
  184.     static final String         SELECTABLE_KEY = "selectable";
  185.     static final String         TRANSPARENT_KEY = "transparent";
  186.     static final String         HTML_PARSING_RULES_KEY = "htmlParsingRules";
  187.     static final String         OWNER_KEY = "owner";
  188.  
  189.     /* constructors */
  190.  
  191.     /** Constructs a TextView. */
  192.     public TextView() {
  193.         this(0, 0, 0, 0);
  194.     }
  195.  
  196.     /** Constructs a TextView with bounds <B>rect</B>.
  197.       */
  198.     public TextView(Rect rect) {
  199.         this(rect.x, rect.y, rect.width, rect.height);
  200.     }
  201.  
  202.     /** Constructs a TextView with bounds
  203.       * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>).
  204.       */
  205.     public TextView(int x, int y, int width, int height) {
  206.  
  207.         super(x, y, width, height);
  208.  
  209.         TextParagraph   newParagraph;
  210.         TextParagraphFormat defaultFormat;
  211.         int i;
  212.  
  213.         _eventVector = new Vector();
  214.         _selection = new TextSelection(this);
  215.  
  216.         _paragraphVector = new Vector();
  217.         _paragraphSpacing = 0;
  218.         _backgroundColor = Color.white;
  219.         _selectionColor = Color.lightGray;
  220.  
  221.         _defaultAttributes = new Hashtable();
  222.         _defaultAttributes.put(FONT_KEY,Font.defaultFont());
  223.         _defaultAttributes.put(TEXT_COLOR_KEY,Color.black);
  224.         _defaultAttributes.put(LINK_COLOR_KEY,Color.blue);
  225.         _defaultAttributes.put(CARET_COLOR_KEY,Color.black);
  226.         _defaultAttributes.put(PRESSED_LINK_COLOR_KEY,Color.red);
  227.         defaultFormat = new TextParagraphFormat();
  228.         defaultFormat.setLeftMargin(3);
  229.         defaultFormat.setRightMargin(3);
  230.         defaultFormat.setJustification(Graphics.LEFT_JUSTIFIED);
  231.  
  232.         int tabPos = 30;
  233.         for(i=0;i < 20 ; i++ ) {
  234.             defaultFormat.addTabPosition(tabPos);
  235.             tabPos += 30;
  236.         }
  237.  
  238.         _defaultAttributes.put(PARAGRAPH_FORMAT_KEY, defaultFormat);
  239.         _wasSelectedRange = new Range(selectedRange());
  240.         newParagraph = new TextParagraph(this);
  241.         newParagraph.addRun(new TextStyleRun(newParagraph, "", null));
  242.         addParagraph(newParagraph);
  243.         reformatAll();
  244.  
  245.         _typingAttributes = new Hashtable();
  246.     }
  247.  
  248.     /** Overridden to take special action when the TextView moves. */
  249.     public void didMoveBy(int deltaX, int deltaY) {
  250.         if (deltaX == 0 && deltaY == 0 && _updateTimer != null) {
  251.             _updateTimer.stop();
  252.             _updateTimer = null;
  253.             _updateParagraph = null;
  254.         }
  255.  
  256.         super.didMoveBy(deltaX, deltaY);
  257.     }
  258.  
  259.     /** Overridden to catch size changes. */
  260.     public void sizeBy(int deltaWidth, int deltaHeight) {
  261.         int origWidth;
  262.         if (!isResizingEnabled()) {
  263.             return;
  264.         }
  265.         origWidth = bounds.width;
  266.         _resizing = true;
  267.         super.sizeBy(deltaWidth, deltaHeight);
  268.         _resizing = false;
  269.  
  270.         if( bounds.width != (origWidth + deltaWidth )) {
  271.           /* This can happen if the scrollbar has been added */
  272.             disableResizing();
  273.             reformatAll();
  274.             enableResizing();
  275.             setDirty(true);
  276.         } else if( deltaWidth != 0 || deltaHeight != 0 ){
  277.             setDirty(true);
  278.         }
  279.     }
  280.  
  281.     /** Overridden to catch size changes. */
  282.     public void didSizeBy(int dw, int dh) {
  283.         if (!_resizing) {
  284.             reformatAll();
  285.         }
  286.         super.didSizeBy(dw, dh);
  287.     }
  288.  
  289.     /** Sets the TextView to be transparent or opaque.
  290.       */
  291.     public void setTransparent(boolean flag) {
  292.         transparent = flag;
  293.     }
  294.  
  295.     /** Overridden to return <b>true</b> if the TextView is transparent.
  296.       * @see #setTransparent
  297.       */
  298.     public boolean isTransparent() {
  299.         return transparent;
  300.     }
  301.  
  302.     /** Overridden to return <b>true</b> - TextViews can be autoscrolled. */
  303.     public boolean wantsAutoscrollEvents() {
  304.         return true;
  305.     }
  306.  
  307.     /** Draws the TextView's contents. */
  308.     public void drawView(Graphics g) {
  309.         TextParagraph   nextParagraph, paragraph;
  310.         Rect            insertionRect, rect;
  311.         int             minY, maxY, count, i;
  312.  
  313.         if (!_drawText) {
  314.             if (_updateParagraph != null) {
  315.                 _updateParagraph.drawLine(g, _updateLine);
  316.             }
  317.         } else {
  318.             count = _paragraphVector.count();
  319.             minY = g.clipRect().y;
  320.             maxY = g.clipRect().maxY();
  321.             rect = Rect.newRect(0, 0, width(), height());
  322.             for (i = 0; i < count; i++) {
  323.                 nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  324.                 if (nextParagraph._y > maxY ||
  325.                     (nextParagraph._y + nextParagraph._height) < minY) {
  326.                     continue;
  327.                 }
  328.                 nextParagraph.drawView(g, rect);
  329.             }
  330.             Rect.returnRect(rect);
  331.         }
  332.  
  333.         if( _selection._insertionPointShowing ) {
  334.             Hashtable attr;
  335.             TextStyleRun run;
  336.             TextPositionInfo insertionPointInfo;
  337.             Rect r = _selection.insertionPointRect();
  338.             Color caretColor = null;
  339.  
  340.             if( g.clipRect().intersects( r )) {
  341.                 insertionPointInfo = _selection.insertionPointInfo();
  342.                 run = _runForIndex( insertionPointInfo._absPosition);
  343.                 if( (attr = run.attributes()) != null)
  344.                     caretColor = (Color) attr.get(CARET_COLOR_KEY);
  345.  
  346.  
  347.                 if( caretColor == null )
  348.                     caretColor = (Color) _defaultAttributes.get(
  349.                                                             CARET_COLOR_KEY);
  350.  
  351.                 if( caretColor == null )
  352.                     caretColor = Color.black;
  353.  
  354.                 g.setColor( caretColor );
  355.                 g.fillRect(r);
  356.             }
  357.         }
  358.     }
  359.  
  360.     /** Implemented to allow TextViews to remove KeyEvents from the
  361.       * EventLoop.
  362.       */
  363.     public Object filterEvents(Vector events) {
  364.         int i;
  365.  
  366.         for(i = 0; i < events.count(); i++) {
  367.             Event event = (Event)events.elementAt(i);
  368.  
  369.             if ((event instanceof KeyEvent) &&
  370.                 (event.type() == KeyEvent.KEY_DOWN)) {
  371.                 if (_filter != null) {
  372.                     if (_filter.acceptsEvent(this, (KeyEvent)event,
  373.                                              _eventVector)) {
  374.                         _eventVector.addElement(event);
  375.                     }
  376.                 } else {
  377.                     _eventVector.addElement(event);
  378.                 }
  379.                 events.removeElementAt(i);
  380.                 i--;
  381.             }
  382.         }
  383.         return null;
  384.     }
  385.  
  386.     /** Overridden to handle mouse clicks within the TextView. */
  387.     public boolean mouseDown(MouseEvent event) {
  388.         TextParagraph           paragraph;
  389.         TextPositionInfo        anchorInfo;
  390.         TextPositionInfo        absoluteAnchorInfo;
  391.         int                     anchorPosition, endPosition;
  392.  
  393.         _mouseDownTextAttachment = null;
  394.         _clickedRange=null;
  395.  
  396.         if(isEditable()|| isSelectable()) /** Should grab focus when only selectable
  397.                                            *  for selection display exclusion
  398.                                            */
  399.             setFocusedView();
  400.  
  401.         if(!rootView().mouseStillDown()) {
  402.             rootView().adjustForExpectedMouseDownCount();
  403.             if(!rootView().mouseStillDown())
  404.                return true;
  405.         }
  406.  
  407.         // set focused view should take care of
  408.         // setting editing to true
  409.         //_setEditing(true);
  410.  
  411.         _selection.hideInsertionPoint();
  412.  
  413.         _clickCount = event.clickCount();
  414.  
  415.         _anchorInfo = positionForPoint(event.x, event.y,false);
  416.         absoluteAnchorInfo = positionForPoint(event.x,event.y,true);
  417.  
  418.         if( _anchorInfo != null && _anchorInfo._endOfLine ) {
  419.             Rect r = new Rect(_anchorInfo._x,_anchorInfo._y,bounds.width,_anchorInfo._lineHeight);
  420.             if(!r.contains(event.x,event.y))
  421.                 selectLineBreak=false;
  422.             else
  423.                 selectLineBreak=true;
  424.         } else
  425.             selectLineBreak = false;
  426.  
  427.         if( absoluteAnchorInfo != null ) {
  428.             TextStyleRun run;
  429.  
  430.             run = _runForIndex( absoluteAnchorInfo._absPosition );
  431.             if( run != null ) {
  432.                 Hashtable attr;
  433.                 TextAttachment item;
  434.                 boolean result;
  435.  
  436.                 attr = run.attributes();
  437.                 if( attr != null ) {
  438.                     if( (item = (TextAttachment)attr.get(TEXT_ATTACHMENT_KEY)) != null && /* End of line ? */
  439.                         run.rangeIndex() == absoluteAnchorInfo._absPosition) {
  440.                         /** Cannot use rectForRange since the height is equal to lineHeight */
  441.                         Rect rect = run.textAttachmentBoundsForOrigin(absoluteAnchorInfo._x,
  442.                                         absoluteAnchorInfo._y,
  443.                                         run._paragraph._baselines[absoluteAnchorInfo._lineNumber]);
  444.                         if( rect != null && rect.contains(event.x,event.y)) {
  445.  
  446.                             result = item.mouseDown(new MouseEvent( event.timeStamp,
  447.                                                                     event.type,
  448.                                                                     event.x - rect.x,
  449.                                                                     event.y - rect.y,
  450.                                                                     event.modifiers));
  451.                             if( result ) {
  452.                                 _mouseDownTextAttachment = item;
  453.                                 _mouseDownTextAttachmentOrigin =
  454.                                     new Point(rect.x,rect.y);
  455.                                 return true;
  456.                             }
  457.                         }
  458.                     }
  459.                     if(!isEditable() && attr.get(LINK_KEY) != null
  460.                         && runUnderMouse(run,event.x,event.y)
  461.                         && _clickCount == 1) {
  462.                         _clickedRange  = linkRangeForPosition( absoluteAnchorInfo._absPosition);
  463.                         highlightLinkWithRange(_clickedRange,true);
  464.                     }
  465.                 }
  466.             }
  467.         }
  468.  
  469.         if( !isSelectable() && _clickedRange == null)
  470.             return false;
  471.  
  472.         _firstRange = null;
  473.         if( _clickCount > 1 ) {
  474.             /* Multiple click at a line break should do nothing */
  475.             if(!selectLineBreak) {
  476.                 switch(_clickCount) {
  477.                 case 2:
  478.                     _firstRange = groupForIndex(_anchorInfo._absPosition);
  479.                     break;
  480.                 default:
  481.                     _firstRange = paragraphForIndex(_anchorInfo._absPosition);
  482.                     break;
  483.                 }
  484.  
  485.                 if( _firstRange != null && !_firstRange.isNullRange()) {
  486.                     if( event.isShiftKeyDown()) {
  487.                         Range r = new Range(selectedRange());
  488.                         r.unionWith(_firstRange.index,_firstRange.length);
  489.                         _selection.setRange(r.index,r.index+r.length,null,false);
  490.                     } else
  491.                         _selection.setRange(_firstRange.index,_firstRange.lastIndex()+1,
  492.                                           null,false);
  493.                     _selectionChanged();
  494.                 }
  495.                 return true;
  496.             } else
  497.                 _firstRange = new Range(_anchorInfo._absPosition,0);
  498.         }
  499.  
  500.         if( event.isShiftKeyDown() ) {
  501.             _selection.setRange(_selection.orderedSelectionStart(),
  502.                                 _anchorInfo._absPosition,null,false);
  503.         } else
  504.             _selection.setInsertionPoint(_anchorInfo);
  505.         _selectionChanged();
  506.         _upInfo = null;
  507.  
  508.         _downY = _anchorInfo._y + _anchorInfo._lineHeight;
  509.  
  510.         return true;
  511.     }
  512.  
  513.     /** Overridden to handle mouse drags within the TextView. */
  514.     public void mouseDragged(MouseEvent event) {
  515.         TextParagraph          paragraph;
  516.         TextPositionInfo        newUp;
  517.         Rect                    tmpRect;
  518.         int                     wordPosition, anchorPosition;
  519.         Point                   realPoint;
  520.         boolean                 selectionChanged;
  521.  
  522.         if( _mouseDownTextAttachment != null ) {
  523.             _mouseDownTextAttachment.mouseDragged( new MouseEvent( event.timeStamp,
  524.                                                                    event.type,
  525.                                                                    event.x - _mouseDownTextAttachmentOrigin.x,
  526.                                                                    event.y - _mouseDownTextAttachmentOrigin.y,
  527.                                                                    event.modifiers));
  528.             return;
  529.         }
  530.  
  531.         realPoint = new Point(event.x,event.y);
  532.         if( realPoint.x >= bounds.width )
  533.             realPoint.x = bounds.width-1;
  534.         else if( realPoint.x < 0 )
  535.             realPoint.x = 0;
  536.  
  537.         if( realPoint.y >= bounds.height )
  538.             realPoint.y = bounds.height-1;
  539.         else if( realPoint.y < 0 )
  540.             realPoint.y = 0;
  541.  
  542.         newUp = positionForPoint(realPoint.x, realPoint.y,false);
  543.  
  544.         if( _clickedRange != null ) {
  545.             TextStyleRun runForMouse = _runForIndex( newUp._absPosition);
  546.             Hashtable runAttr;
  547.             if( (runAttr = runForMouse.attributes()) != null &&
  548.                 (runAttr.get(LINK_KEY) != null ) && runUnderMouse(runForMouse,event.x,event.y)) {
  549.                 Range newClickedRange = linkRangeForPosition(newUp._absPosition);
  550.                 if(! newClickedRange.equals(_clickedRange)) {
  551.                     highlightLinkWithRange(_clickedRange,false);
  552.                     _clickedRange = newClickedRange;
  553.                     highlightLinkWithRange(_clickedRange,true);
  554.                 }
  555.                 return;
  556.             } else {
  557.                 highlightLinkWithRange(_clickedRange,false);
  558.                 _clickedRange = null;
  559.             }
  560.         }
  561.  
  562.         if(!isSelectable())
  563.             return;
  564.  
  565.  
  566.         if (newUp == null) {
  567.             // THE MOUSE DRAGGED GOT A NULL POINT
  568.             return;
  569.         }
  570.  
  571.         if (!containsPointInVisibleRect(event.x, event.y)) {
  572.             tmpRect = TextView.newRect(newUp._x, newUp._y, 1,
  573.                                        newUp._lineHeight);
  574.             scrollRectToVisible(tmpRect);
  575.             TextView.returnRect(tmpRect);
  576.         }
  577.  
  578.         anchorPosition = _anchorInfo._absPosition;
  579.         if( _upInfo != null && newUp._absPosition != _upInfo._absPosition )
  580.             selectionChanged = true;
  581.         else
  582.             selectionChanged = false;
  583.         _upInfo = newUp;
  584.  
  585.  
  586.         if(selectionChanged) {
  587.             Range first,last;
  588.  
  589.             switch( _clickCount ) {
  590.             case 0:
  591.             case 1:
  592.                 _selection.setRange(anchorPosition, _upInfo._absPosition, _upInfo,
  593.                                     selectLineBreak);
  594.                 _selectionChanged();
  595.                 return;
  596.             case 2:
  597.                 last  = groupForIndex(_upInfo._absPosition);
  598.                 break;
  599.             default:
  600.                 last  = paragraphForIndex(_upInfo._absPosition);
  601.                 break;
  602.             }
  603.             if(_firstRange != null && !_firstRange.isNullRange() && !last.isNullRange() ) {
  604.                 last.unionWith(_firstRange);
  605.                 if(!last.equals(selectedRange())) {
  606.                     _selection.setRange(last.index,last.lastIndex()+1,null,selectLineBreak);
  607.                     _selectionChanged();
  608.                 }
  609.             }
  610.         }
  611.     }
  612.  
  613.     /** Overridden to handle mouse up Events within the TextView. */
  614.     public void mouseUp(MouseEvent event) {
  615.         TextPositionInfo upPosition;
  616.  
  617.         if( _mouseDownTextAttachment != null ) {
  618.             _mouseDownTextAttachment.mouseUp( new MouseEvent( event.timeStamp,
  619.                                                               event.type,
  620.                                                               event.x - _mouseDownTextAttachmentOrigin.x,
  621.                                                               event.y - _mouseDownTextAttachmentOrigin.y,
  622.                                                               event.modifiers));
  623.             _mouseDownTextAttachment = null;
  624.             _mouseDownTextAttachmentOrigin = null;
  625.             return;
  626.         }
  627.  
  628.         if( _clickedRange != null ) {
  629.             Range newClickedRange;
  630.             upPosition = positionForPoint(event.x,event.y,true);
  631.             newClickedRange = linkRangeForPosition(upPosition._absPosition);
  632.             highlightLinkWithRange(_clickedRange,false);
  633.  
  634.             if( newClickedRange != null && newClickedRange.equals(_clickedRange)) {
  635.                 if( _owner != null ) {
  636.                     TextStyleRun run = _runForIndex( _clickedRange.index );
  637.                     Hashtable runAttr = run.attributes();
  638.                     String urlStr = null;
  639.  
  640.                     if( runAttr != null && (urlStr = (String) runAttr.get(LINK_KEY)) != null
  641.                         && runsUnderMouse(runsForRange(_clickedRange),event.x,event.y))
  642.                         _owner.linkWasSelected(this,_clickedRange,urlStr);
  643.                 }
  644.             }
  645.             _clickedRange=null;
  646.         }
  647.  
  648.         if(!isSelectable())
  649.             return;
  650.  
  651.         if (_upInfo == null ||
  652.             _upInfo._absPosition == _anchorInfo._absPosition) {
  653.             _selection.showInsertionPoint();
  654.         }
  655.  
  656.         _firstRange = null;
  657.     }
  658.  
  659.     /** Overridden to return TEXT_CURSOR when the TextView is
  660.       * selectable or editable.
  661.       */
  662.     public int cursorForPoint(int x, int y) {
  663.         if( isEditable() )
  664.             return TEXT_CURSOR;
  665.         else {
  666.             TextPositionInfo info = positionForPoint(x, y, true);
  667.             if (info != null) {
  668.               TextStyleRun run = _runForIndex(info._absPosition);
  669.  
  670.               if (run != null) {
  671.                 Hashtable attr = run.attributes();
  672.  
  673.                 if (attr != null) {
  674.                   if (attr.get(LINK_KEY) != null && runUnderMouse(run,x,y)) {
  675.                     return HAND_CURSOR;
  676.                   }
  677.                 }
  678.               }
  679.             }
  680.  
  681.             if (isSelectable()) {
  682.               return TEXT_CURSOR;
  683.             } else {
  684.               return ARROW_CURSOR;
  685.             }
  686.         }
  687.     }
  688.  
  689.     /** Implements TextView's commands.  You should never
  690.       * call this method.
  691.       */
  692.     public void performCommand(String command, Object data) {
  693.         Rect    updateRect;
  694.         int     index;
  695.  
  696.         if( command.equals("refreshBitmap")) {
  697.             refreshBitmap(data);
  698.         } else if (command != null && command.equals(SET_FONT)) {
  699.             processSetFont((Font)data);
  700.             return;
  701.         } else if (command.equals(CUT)) {
  702.             cut();
  703.         } else if (command.equals(COPY)) {
  704.             copy();
  705.         } else if (command.equals(PASTE)) {
  706.             paste();
  707.         } else if (!(data instanceof Timer)) {
  708.             return;
  709.         }
  710.  
  711.         /* more to redraw? */
  712.         if (_updateParagraph == null) {
  713.             if (_updateTimer != null) {
  714.                 _updateTimer.stop();
  715.                 _updateTimer = null;
  716.             }
  717.             return;
  718.         }
  719.  
  720.         _drawText = false;
  721.         updateRect = _updateParagraph.rectForLine(_updateLine);
  722.         draw(updateRect);
  723.         TextView.returnRect(updateRect);
  724.         _drawText = true;
  725.  
  726.         /* more to redraw? */
  727.         _updateLine++;
  728.         if (_updateLine >= _updateParagraph._breakCount) {
  729.             if (!_drawNextParagraph) {
  730.                 _updateParagraph = null;
  731.             } else {
  732.                 index = _paragraphVector.indexOfIdentical(_updateParagraph) + 1;
  733.                 if (index == 0 || index >= _paragraphVector.count()) {
  734.                     _updateParagraph = null;
  735.                 } else {
  736.                     _updateParagraph =
  737.                         (TextParagraph)_paragraphVector.elementAt(index);
  738.                     _updateLine = 0;
  739.                 }
  740.             }
  741.         }
  742.  
  743.         /* if not, stop */
  744.         if (_updateParagraph == null && _updateTimer != null) {
  745.             _updateTimer.stop();
  746.             _updateTimer = null;
  747.         }
  748.     }
  749.  
  750.     /** Implemented so that TextView can participate in the TargetChain.
  751.       * @see ExtendedTarget
  752.       */
  753.     public boolean canPerformCommand(String command) {
  754.         if (command.equals(SET_FONT)) {
  755.             if( usesSingleFont() || !isEditable())
  756.                 return false;
  757.             else
  758.                 return true;
  759.         } else if (command.equals("refreshBitmap") ||
  760.                    command.equals(COPY) ||
  761.                    (isEditable() && command.equals(CUT)) ||
  762.                    (isEditable() && command.equals(PASTE))) {
  763.             return true;
  764.         } else {
  765.             return false;
  766.         }
  767.     }
  768.  
  769.     /** Overridden so that TextView can receive KeyEvents. */
  770.     public void keyDown(KeyEvent event) {
  771.       if(Application.application().handleExtendedKeyEvent() &&
  772.          JDK11AirLock.hasOneOneEvents()) {
  773.         if(event.isPrintableKey() && !event.isDeleteKey())
  774.           return;
  775.         else if((event.modifiers & KeyEvent.ALT_MASK) == KeyEvent.ALT_MASK)
  776.           return;
  777.         else if(event.isBackspaceKey())
  778.           return;
  779.       }
  780.       processKey(event);
  781.     }
  782.  
  783.     /** Overridden so that TextView can receive KeyEvents. */
  784.     public void keyTyped(KeyEvent event) {
  785.         processKey(event);
  786.      }
  787.  
  788.  
  789.       void processKey(KeyEvent event) {
  790.         if( event.isPageUpKey()) {
  791.             int newY;
  792.             Rect visibleRect = new Rect();
  793.             TextPositionInfo newPosition;
  794.  
  795.             computeVisibleRect(visibleRect);
  796.             visibleRect.y -= (visibleRect.height - 1);
  797.             if( visibleRect.y < 0 )
  798.                 visibleRect.y = 0;
  799.  
  800.  
  801.             newPosition = positionForPoint(visibleRect.x,visibleRect.y,true);
  802.             if( newPosition != null)
  803.                 visibleRect.y = newPosition._y;
  804.             scrollRectToVisible(visibleRect);
  805.             return;
  806.         } else if( event.isPageDownKey()) {
  807.             int newY;
  808.             Rect visibleRect = new Rect();
  809.             TextPositionInfo currentPosition;
  810.             TextPositionInfo newPosition;
  811.  
  812.  
  813.  
  814.             computeVisibleRect(visibleRect);
  815.             currentPosition = positionForPoint(visibleRect.x,visibleRect.y,true);
  816.  
  817.             visibleRect.y += (visibleRect.height - 1);
  818.             if( visibleRect.y > (bounds.height - visibleRect.height))
  819.                 visibleRect.y = (bounds.height - visibleRect.height);
  820.  
  821.  
  822.  
  823.             newPosition = positionForPoint(visibleRect.x,visibleRect.y,true);
  824.             if( newPosition != null) {
  825.                 visibleRect.y = newPosition._y;
  826.                 if( currentPosition != null &&
  827.                     currentPosition._absPosition == newPosition._absPosition)
  828.                     visibleRect.y += newPosition._lineHeight;
  829.             }
  830.             scrollRectToVisible(visibleRect);
  831.             return;
  832.         }
  833.  
  834.         if(! isEditable() ) {
  835.             //            Application.application().beep();
  836.             return;
  837.         }
  838.  
  839.         if(! hasSelection()) {
  840.             //            Application.application().beep();
  841.             return;
  842.         }
  843.  
  844.         if (_filter != null) {
  845.             if (_filter.acceptsEvent(this, event, _eventVector)) {
  846.                 _eventVector.addElement(event);
  847.             }
  848.         } else {
  849.             _eventVector.addElement(event);
  850.         }
  851.  
  852.         application().eventLoop().filterEvents(this);
  853.         while (!_eventVector.isEmpty()) {
  854.             _keyDown();
  855.         }
  856.     }
  857.  
  858.     /** Overridden to let TextViews accepts drags of Colors and Images.
  859.      */
  860.     public DragDestination acceptsDrag(DragSession session, int x, int y) {
  861.         String type = session.dataType();
  862.  
  863.         if (isEditable() && hasSelection() &&
  864.             (Color.COLOR_TYPE.equals(type) ||
  865.              Image.IMAGE_TYPE.equals(type))) {
  866.             return this;
  867.         } else {
  868.             return null;
  869.         }
  870.     }
  871.  
  872.     /** DragDestination support method.
  873.       * @see DragDestination#dragEntered
  874.       */
  875.     public boolean dragEntered(DragSession session) {
  876.         return true;
  877.     }
  878.  
  879.     /** DragDestination support method.
  880.       * @see DragDestination#dragMoved
  881.       */
  882.     public boolean dragMoved(DragSession session) {
  883.         return true;
  884.     }
  885.  
  886.     /** DragDestination support method.
  887.       * @see DragDestination#dragExited
  888.       */
  889.     public void dragExited(DragSession session) {
  890.     }
  891.  
  892.     /** Drag and drop destination support method, called when the user
  893.       * releases a DragSession's Image over the TextView. If the DragSession
  894.       * represents a Color, changes the currently selected text's Color to that
  895.       * Color. If the DragSession represents an Image, replaces the selected
  896.       * text with the Image.
  897.       * @see DragDestination#dragExited
  898.       */
  899.     public boolean dragDropped(DragSession session) {
  900.         TextParagraph           insertionParagraph;
  901.         TextPositionInfo        insertionPoint;
  902.         TextStyleRun            newRun;
  903.         Object                  dragItem;
  904.         int                     index;
  905.         Range                   r;
  906.  
  907.  
  908.         if (!isEditable() || !hasSelection()) {
  909.             return false;
  910.         }
  911.  
  912.         dragItem = session.data();
  913.         if (dragItem == null)
  914.             return false;
  915.  
  916.         if (dragItem instanceof Color) {
  917.             r = selectedRange();
  918.             if( r.length > 0 )
  919.                 addAttributeForRange( TEXT_COLOR_KEY, dragItem , r);
  920.             else
  921.                 addTypingAttribute(TEXT_COLOR_KEY, dragItem);
  922.             return true;
  923.         } else if(dragItem instanceof Image) {
  924.             replaceRangeWithTextAttachment(selectedRange(),
  925.                                            new ImageAttachment((Image)dragItem));
  926.             return true;
  927.         }
  928.         return false;
  929.     }
  930.  
  931.     /** Focus management support method. */
  932.     public void startFocus() {
  933.         _setEditing(true);
  934.         showInsertionPoint();
  935.         _selection._startFlashing();
  936.         if( isEditable() && _owner != null )
  937.             _owner.textEditingDidBegin(this);
  938.         if( hasSelection() ) {
  939.             Range r = selectedRange();
  940.             if( r.length > 0 )
  941.                 dirtyRange(r);
  942.         } else
  943.             selectRange(new Range(0,0));
  944.     }
  945.  
  946.     /** Focus management support method. */
  947.     public void stopFocus() {
  948.         _selection._stopFlashing();
  949.         hideInsertionPoint();
  950.         _setEditing(false);
  951.         if( isEditable() && _owner != null )
  952.             _owner.textEditingDidEnd(this);
  953.         if( hasSelection() ) {
  954.             Range r = selectedRange();
  955.             if( r.length > 0 )
  956.                 dirtyRange(r);
  957.         }
  958.     }
  959.  
  960.     /** Focus management support method. */
  961.     public void pauseFocus() {
  962.         _selection._stopFlashing();
  963.         hideInsertionPoint();
  964.     }
  965.  
  966.     /** Focus management support method. */
  967.     public void resumeFocus() {
  968.         showInsertionPoint();
  969.         _selection._startFlashing();
  970.     }
  971.  
  972.  
  973.     /** Converts the TextView's contents to a String containing both the text
  974.       * and its attributes.
  975.       */
  976.     public String toString() {
  977.         int i,c;
  978.         StringBuffer sb = new StringBuffer();
  979.  
  980.         for(i=0,c=_paragraphVector.count() ; i < c ; i++ )
  981.             sb.append(_paragraphVector.elementAt(i).toString());
  982.  
  983.         return sb.toString();
  984.     }
  985.  
  986.     /** Sets the TextView's filter, the object that examines each KeyEvent
  987.       * the TextView receives.
  988.       */
  989.     public void setFilter(TextFilter aFilter) {
  990.         _filter = aFilter;
  991.     }
  992.  
  993.     /** Returns the TextView's text filter.
  994.       * @see #setFilter
  995.       */
  996.     public TextFilter filter() {
  997.         return _filter;
  998.     }
  999.  
  1000.     /** Sets the TextField's owner, the object that it notifies of important
  1001.       * events such as the selection changing.
  1002.       * @see TextViewOwner
  1003.       */
  1004.     public void setOwner(TextViewOwner owner) {
  1005.         _owner = owner;
  1006.     }
  1007.  
  1008.     /** Returns the TextField's owner.
  1009.       * @see #setOwner
  1010.       */
  1011.     public TextViewOwner owner() {
  1012.         return _owner;
  1013.     }
  1014.  
  1015.     /** Disables TextView resizing.  If you are about to perform several
  1016.       * changes to the TextView, call this method to disable the auto-resizing,
  1017.       * to avoid recomputing the TextView's size.
  1018.       * @see #enableResizing
  1019.       */
  1020.     public void disableResizing() {
  1021.         _resizeDisabled++;
  1022.     }
  1023.  
  1024.     /** Reenables TextView resizing.  You should call <b>sizeToMinSize()</b>
  1025.       * to get the TextView to relayout its contents.
  1026.       *  @see #disableResizing
  1027.       */
  1028.     public void enableResizing() {
  1029.         _resizeDisabled--;
  1030.         if (_resizeDisabled < 0) {
  1031.             _resizeDisabled = 0;
  1032.  
  1033.         }
  1034.     }
  1035.  
  1036.     /** Returns <b>true</b> if resizing is enabled.
  1037.       * @see #enableResizing
  1038.       * @see #disableResizing
  1039.       */
  1040.     public boolean isResizingEnabled() {
  1041.         return _resizeDisabled == 0;
  1042.     }
  1043.  
  1044.     /** Calculates the minimum size needed to display the text and
  1045.       * resizes the TextView to this minimum size.
  1046.       */
  1047.     public void sizeToMinSize() {
  1048.         sizeBy(0, adjustCharCountsAndSpacing() - bounds.height);
  1049.     }
  1050.  
  1051.  
  1052.     /** Scrolls the TextView so that <b>aRange</b> is visible. */
  1053.     public void scrollRangeToVisible(Range aRange) {
  1054.         if(aRange.index == 0 && aRange.length == 0) {  /* top ?*/
  1055.             scrollRectToVisible(new Rect(0,0,1,1));
  1056.         } else if(aRange.index >= length()) {
  1057.             scrollRectToVisible(new Rect(width()-1,height()-1,1,1));
  1058.         } else if(aRange.length == 0) {
  1059.             TextPositionInfo top = positionInfoForIndex(aRange.index);
  1060.             scrollRectToVisible(new Rect(top._x,top._y+top._lineHeight,1,top._lineHeight));
  1061.         } else {
  1062.             TextPositionInfo top = positionInfoForIndex(aRange.index);
  1063.             TextPositionInfo bottom = positionInfoForIndex(aRange.index + aRange.length);
  1064.             int y1,y2;
  1065.  
  1066.             y1 = top._y;
  1067.             y2 = bottom._y + bottom._lineHeight;
  1068.  
  1069.             if(top._y == bottom._y) {
  1070.                 scrollRectToVisible(new Rect(top._x,y1,bottom._x - top._x,y2-y1));
  1071.             } else {
  1072.                 scrollRectToVisible(new Rect(0,y1,width(),y2 - y1));
  1073.             }
  1074.         }
  1075.     }
  1076.  
  1077.     /** If <b>flag</b> is <b>true</b>, TextView ignores all the font
  1078.       * attributes but the default one, which it uses to display the
  1079.       * text.
  1080.       */
  1081.     public void setUseSingleFont(boolean flag) {
  1082.         _useSingleFont = flag;
  1083.     }
  1084.  
  1085.     /** Returns <b>true</b> if the receiver uses a single font.
  1086.       * @see #setUseSingleFont
  1087.       */
  1088.     public boolean usesSingleFont() {
  1089.         return _useSingleFont;
  1090.     }
  1091.  
  1092.     /** Sets the default attributes used by the TextView when new text is
  1093.       * inserted.
  1094.       */
  1095.     public void setDefaultAttributes(Hashtable attributes) {
  1096.         int i,c;
  1097.         Range r;
  1098.  
  1099.         _defaultAttributes = attributes;
  1100.         if( attributes.get(FONT_KEY) != null )
  1101.             _defaultFontMetricsCache = null;
  1102.  
  1103.         if( attributes.get(PARAGRAPH_FORMAT_KEY) == null ) {
  1104.             TextParagraphFormat defaultFormat;
  1105.  
  1106.             defaultFormat = new TextParagraphFormat();
  1107.             defaultFormat.setLeftMargin(3);
  1108.             defaultFormat.setRightMargin(3);
  1109.             defaultFormat.setJustification(Graphics.LEFT_JUSTIFIED);
  1110.             _defaultAttributes.put(PARAGRAPH_FORMAT_KEY, defaultFormat);
  1111.         }
  1112.  
  1113.         reformatAll();
  1114.  
  1115.         r = allocateRange(0,length());
  1116.         dirtyRange( r );
  1117.         recycleRange( r );
  1118.     }
  1119.  
  1120.     /** Returns the default attributes.
  1121.       * @see #setDefaultAttributes
  1122.       */
  1123.     public Hashtable defaultAttributes() {
  1124.         return _defaultAttributes;
  1125.     }
  1126.  
  1127.     /** Add an attribute into typingAttributes.
  1128.      *  When the user types some character, attributes into typingAttributes
  1129.      *  are added.
  1130.      */
  1131.     public void addTypingAttribute(String key,Object value) {
  1132.         Hashtable tmp = new Hashtable();
  1133.         Enumeration keys;
  1134.         String k;
  1135.  
  1136.         tmp.put(key,value);
  1137.         tmp = TextView.attributesByRemovingStaticAttributes(tmp);
  1138.         keys = tmp.keys();
  1139.  
  1140.         while(keys.hasMoreElements()){
  1141.             k = (String)keys.nextElement();
  1142.             _typingAttributes.put(k,tmp.get(k));
  1143.         }
  1144.     }
  1145.  
  1146.     /** Return the current typing attributes
  1147.       *
  1148.       */
  1149.     public Hashtable typingAttributes() {
  1150.         return _typingAttributes;
  1151.     }
  1152.  
  1153.     /** Set the typing attributes.
  1154.       *
  1155.       */
  1156.     public void setTypingAttributes(Hashtable attr) {
  1157.         if( attr == null )
  1158.             _typingAttributes = new Hashtable();
  1159.         else
  1160.             _typingAttributes = TextView.attributesByRemovingStaticAttributes(attr);
  1161.     }
  1162.  
  1163.     /** Sets the TextView's background Color. */
  1164.     public void setBackgroundColor(Color aColor) {
  1165.         if (aColor != null) {
  1166.             _backgroundColor = aColor;
  1167.         }
  1168.     }
  1169.  
  1170.     /** Returns the TextView's background Color.
  1171.       * @see #setBackgroundColor
  1172.       */
  1173.     public Color backgroundColor() {
  1174.         return _backgroundColor;
  1175.     }
  1176.  
  1177.     /** Sets the TextView's selection Color. */
  1178.     public void setSelectionColor(Color aColor) {
  1179.         if (aColor != null) {
  1180.             _selectionColor = aColor;
  1181.         }
  1182.     }
  1183.  
  1184.     /** Returns the TextView's selection Color.
  1185.       * @see #setSelectionColor
  1186.       */
  1187.     public Color selectionColor() {
  1188.         return _selectionColor;
  1189.     }
  1190.  
  1191.     /** Sets whether or not the TextView is editable. */
  1192.     public void setEditable(boolean flag) {
  1193.         if (_editable != flag) {
  1194.             _editable = flag;
  1195.             setSelectable(true);
  1196.         }
  1197.     }
  1198.  
  1199.     /** Returns <b>true</b> if the TextView is editable.
  1200.       * @see #setEditable
  1201.       */
  1202.     public boolean isEditable() {
  1203.         return _editable;
  1204.     }
  1205.  
  1206.     /** Sets whether or not the TextView is selectable. */
  1207.     public void setSelectable(boolean flag) {
  1208.         RootView        rootView;
  1209.  
  1210.         if (_selectable != flag) {
  1211.             _selectable = flag;
  1212.             rootView = rootView();
  1213.             if (rootView != null) {
  1214.                 rootView.updateCursor();
  1215.             }
  1216.         }
  1217.     }
  1218.  
  1219.     /** Returns <b>true</b> if the TextView is selectable.
  1220.       * @see #setSelectable
  1221.       */
  1222.     public boolean isSelectable() {
  1223.         return _selectable;
  1224.     }
  1225.  
  1226.     /** Sets the font associated with the default attributes. */
  1227.     public void setFont(Font aFont) {
  1228.         if (aFont == null) {
  1229.             return;
  1230.         }
  1231.         addDefaultAttribute( FONT_KEY, aFont);
  1232.     }
  1233.  
  1234.     /** Returns the font associated with the default attributes.
  1235.       * @see #setFont
  1236.       */
  1237.     public Font font() {
  1238.         return (Font) _defaultAttributes.get(FONT_KEY);
  1239.     }
  1240.  
  1241.  
  1242.     /** Sets the TextView's default text Color. */
  1243.     public void setTextColor(Color aColor) {
  1244.         if (aColor != null) {
  1245.             addDefaultAttribute( TEXT_COLOR_KEY , aColor );
  1246.         }
  1247.     }
  1248.  
  1249.  
  1250.     /** Returns the TextView's default text Color.
  1251.       * @see #setTextColor
  1252.       */
  1253.     public Color textColor() {
  1254.         Color res = (Color) _defaultAttributes.get(TEXT_COLOR_KEY);
  1255.         if( res == null )
  1256.             return Color.black;
  1257.         else
  1258.             return res;
  1259.     }
  1260.  
  1261.     /** Sets the TextView's caret Color.  Default is Color.black.
  1262.      */
  1263.     public void setCaretColor(Color aColor) {
  1264.         if( aColor != null ) {
  1265.             addDefaultAttribute( CARET_COLOR_KEY , aColor);
  1266.         }
  1267.  
  1268.     }
  1269.  
  1270.     /** Returns the TextView's caret Color.
  1271.       * @see #setCaretColor
  1272.       */
  1273.     public Color caretColor() {
  1274.         Color res = (Color) _defaultAttributes.get(CARET_COLOR_KEY);
  1275.         if( res == null )
  1276.             return Color.black;
  1277.         else
  1278.             return res;
  1279.     }
  1280.  
  1281.     /** Replaces the string enclosed by <b>r</b> with <b>aString</b>.  If
  1282.       * <b>r</b> is a null range, this method inserts <b>aString</b> into
  1283.       * the text.  If <b>aString</b> is
  1284.       * <b>null</b> or "", this method removes the text defined by <b>r</b>.
  1285.       * The inserted text will have the attributes set for the first
  1286.       * index of the range. If the index is 0, default attributes will be used.
  1287.       */
  1288.     public void replaceRangeWithString(Range r,String aString) {
  1289.         String                          subString;
  1290.         int                             insertionPoint, newHeight, start, end,
  1291.                                         lastIndex;
  1292.         boolean                         insertReturn = true;
  1293.         int                             paragraphIndex;
  1294.  
  1295.         if( _owner != null )
  1296.             _owner.textWillChange(this,r);
  1297.  
  1298.         if( r.equals(new Range(0,length()))) {
  1299.           replaceContentWithString(aString);
  1300.           if( _owner != null )
  1301.               _owner.textDidChange(this,new Range(0,length()));
  1302.           return;
  1303.         }
  1304.  
  1305.         disableResizing();
  1306.         paragraphIndex = _paragraphIndexForIndex(r.index);
  1307.  
  1308.         deleteRange(r,null);
  1309.  
  1310.         if( aString == null || aString.equals("") ) {
  1311.             enableResizing();
  1312.             if(paragraphIndex > 0)
  1313.               sizeBy(0,adjustCharCountsAndSpacing(paragraphIndex-1) - bounds.height);
  1314.             else
  1315.               sizeToMinSize();
  1316.             if( _owner != null )
  1317.                 _owner.textDidChange(this,new Range(r.index,0));
  1318.             return;
  1319.         }
  1320.  
  1321.         disableAttachmentNotification();
  1322.         insertionPoint = r.index;
  1323.         start = 0;
  1324.         end   = aString.indexOf('\n');
  1325.         if (end == -1) {
  1326.             insertString(aString,insertionPoint);
  1327.             enableResizing();
  1328.             if(paragraphIndex > 0)
  1329.               sizeBy(0,adjustCharCountsAndSpacing(paragraphIndex-1) - bounds.height);
  1330.             else
  1331.               sizeToMinSize();
  1332.             enableAttachmentNotification();
  1333.             if( _owner != null )
  1334.                 _owner.textDidChange(this,new Range(r.index,aString.length()));
  1335.             return;
  1336.         }
  1337.  
  1338.         insertString(aString.substring(start, end),insertionPoint,true);
  1339.         insertReturn(insertionPoint + (end-start));
  1340.         insertionPoint += ((end - start) + 1); /* Plus one for the carriage return */
  1341.         lastIndex = aString.length()-1;
  1342.         while(end < lastIndex) {
  1343.  
  1344.             start = end + 1;
  1345.             end   = aString.indexOf('\n', start);
  1346.             if( end == -1 ) {
  1347.                 end = lastIndex+1;
  1348.                 insertReturn = false;
  1349.             }
  1350.  
  1351.             if( end > start ) {
  1352.                 subString = aString.substring(start,end);
  1353.                 insertString(subString,insertionPoint,true);
  1354.                 if( insertReturn )
  1355.                     insertReturn(insertionPoint + (end - start));
  1356.                 insertionPoint += ((end - start)+1);
  1357.             } else {
  1358.                 insertReturn(insertionPoint);
  1359.                 insertionPoint++;
  1360.             }
  1361.         }
  1362.  
  1363.         enableResizing();
  1364.         if(paragraphIndex > 0)
  1365.           sizeBy(0,adjustCharCountsAndSpacing(paragraphIndex-1) - bounds.height);
  1366.         else
  1367.           sizeToMinSize();
  1368.         enableAttachmentNotification();
  1369.         if( _owner != null )
  1370.             _owner.textDidChange(this,new Range(r.index,aString.length()));
  1371.     }
  1372.  
  1373.     /** Returns the string included in <b>r</b>. */
  1374.     public String stringForRange(Range r) {
  1375.         TextParagraph leftP = _paragraphForIndex(r.index);
  1376.         TextParagraph rightP = _paragraphForIndex(r.index + r.length);
  1377.  
  1378.         if( leftP == null )
  1379.             return null;
  1380.  
  1381.         if( rightP == null )
  1382.             rightP = lastParagraph();
  1383.  
  1384.         if( leftP == rightP )
  1385.             return leftP.stringForRange(r);
  1386.         else {
  1387.             TextParagraph p;
  1388.             StringBuffer sb = new StringBuffer();
  1389.             Range  intersection = allocateRange();
  1390.             String s;
  1391.             int i,c;
  1392.  
  1393.             for( i = _paragraphVector.indexOfIdentical(leftP),
  1394.                      c = _paragraphVector.indexOfIdentical(rightP) ; i <= c ; i++ ) {
  1395.                 intersection.index  = r.index;
  1396.                 intersection.length = r.length;
  1397.                 p = (TextParagraph) _paragraphVector.elementAt(i);
  1398.                 intersection.intersectWith( p.range() );
  1399.                 sb.append( p.stringForRange( intersection ));
  1400.             }
  1401.  
  1402.             recycleRange( intersection );
  1403.             return sb.toString();
  1404.         }
  1405.     }
  1406.  
  1407.  
  1408.     /** Sets the attributes for the Range <b>r</b>. <b>attributes</b> is a
  1409.       * Hashtable containing any attributes. TextView defines some special
  1410.       * attributes that it interprets when laying out and displaying the text.
  1411.       */
  1412.     public void setAttributesForRange(Hashtable attributes,Range r) {
  1413.         Range  paragraphsRange;
  1414.         Vector runsVector;
  1415.         TextStyleRun run;
  1416.         int i,c;
  1417.  
  1418.         paragraphsRange = paragraphsRangeForRange(r);
  1419.         for(i=paragraphsRange.index,c=paragraphsRange.index + paragraphsRange.length ;
  1420.             i < c ; i++ ) {
  1421.             ((TextParagraph)_paragraphVector.elementAt(i)).setFormat(null);
  1422.         }
  1423.  
  1424.         runsVector = createAndReturnRunsForRange(r);
  1425.         for(i=0,c=runsVector.count() ; i < c ; i++ ) {
  1426.             run = (TextStyleRun) runsVector.elementAt(i);
  1427.             run.setAttributes(null);
  1428.         }
  1429.  
  1430.         addAttributesForRange(attributes,r);
  1431.     }
  1432.  
  1433.  
  1434.     /** Returns the attributes for the character at <b>anIndex</b>.
  1435.       * To determine the scope of the attributes, use
  1436.       * <b>runRangeForCharacterAtIndex()</b>
  1437.       * and <b>paragraphRangeForCharacterAtIndex()</b>.
  1438.       */
  1439.     public Hashtable attributesAtIndex(int anIndex) {
  1440.         TextStyleRun r = _runForIndex(anIndex);
  1441.         TextParagraph p = _paragraphForIndex(anIndex);
  1442.  
  1443.         if( r != null && p != null ) {
  1444.             Hashtable attr = r.attributes();
  1445.             if( attr == null ) {
  1446.                 TextParagraphFormat f = p.format();
  1447.                 if( f == null )
  1448.                     return _defaultAttributes;
  1449.                 else {
  1450.                     Hashtable h = (Hashtable) _defaultAttributes.clone();
  1451.                     h.put(PARAGRAPH_FORMAT_KEY,f);
  1452.                     return h;
  1453.                 }
  1454.             } else {
  1455.                 TextParagraphFormat f = p.format();
  1456.                 Hashtable h = (Hashtable)attr.clone();
  1457.  
  1458.                 if( f != null )
  1459.                     h.put(PARAGRAPH_FORMAT_KEY,f);
  1460.                 else
  1461.                     h.put(PARAGRAPH_FORMAT_KEY,_defaultAttributes.get(PARAGRAPH_FORMAT_KEY));
  1462.                 return h;
  1463.             }
  1464.         } else
  1465.             return _defaultAttributes;
  1466.     }
  1467.  
  1468.     /** Returns the number of characters the TextView contains. */
  1469.     public int length() {
  1470.         /* A paragraph has always a \n. The last one does not exists */
  1471.         return _charCount - 1;
  1472.     }
  1473.  
  1474.     /** Returns the TextView's contents as a String. */
  1475.     public String string() {
  1476.         String result;
  1477.         Range r = allocateRange(0,length());
  1478.         result = stringForRange( r );
  1479.         recycleRange( r );
  1480.         return result;
  1481.     }
  1482.  
  1483.     /** Replaces all the text in the TextView with <b>textString</b>. */
  1484.     public void setString(String textString) {
  1485.         Range r = allocateRange(0,length());
  1486.         replaceRangeWithString(r,textString);
  1487.         recycleRange( r );
  1488.     }
  1489.  
  1490.  
  1491.  
  1492.     /** Appends <b>aString</b> to the end of the TextView. Returns the range
  1493.       * of the inserted string.
  1494.       */
  1495.     public Range appendString(String aString) {
  1496.         Range r = allocateRange( length(), 0 );
  1497.         replaceRangeWithString(r , aString);
  1498.         r.length = aString.length();
  1499.         return r;
  1500.     }
  1501.  
  1502.  
  1503.     /** Replaces the string enclosed by <b>r</b> with a <b>aTextAttachment</b>.
  1504.       * Inserts an attachment character.
  1505.       */
  1506.     public void replaceRangeWithTextAttachment(Range r, TextAttachment
  1507.                                                             aTextAttachment) {
  1508.         Hashtable h;
  1509.         Range r2;
  1510.         replaceRangeWithString(r,TEXT_ATTACHMENT_STRING);
  1511.         h = new Hashtable();
  1512.         h.put(TextView.TEXT_ATTACHMENT_KEY,aTextAttachment);
  1513.         r2 = allocateRange(r.index,1);
  1514.         addAttributesForRange(h, r2);
  1515.         recycleRange( r2 );
  1516.     }
  1517.  
  1518.  
  1519.     /** Adds attributes for the text in the Range <b>range</b>.
  1520.       * Merges the contents of <b>attributes</b> with the current attributes
  1521.       * of the range.
  1522.       */
  1523.     public void addAttributesForRange(Hashtable attributes, Range range) {
  1524.         if( _owner != null )
  1525.             _owner.attributesWillChange(this,range);
  1526.  
  1527.         addAttributesForRangeWithoutNotification(attributes,range);
  1528.  
  1529.         if( _owner != null )
  1530.             _owner.attributesDidChange(this,range);
  1531.     }
  1532.  
  1533.     /** Adds the attribute <b>attribute</b> with value <b>value</b> to the
  1534.       * Range <b>range</b>.
  1535.       */
  1536.     public void addAttributeForRange(String attribute, Object value,
  1537.                                      Range range) {
  1538.         Hashtable h = new Hashtable();
  1539.  
  1540.         h.put(attribute, value);
  1541.         addAttributesForRange(h ,range);
  1542.     }
  1543.  
  1544.     /** Removes the attribute <b>attribute</b> from the Range <b>r</b>. */
  1545.     public void removeAttributeForRange(String attribute, Range r) {
  1546.         Vector runVector = runsForRange(r);
  1547.         int i,c;
  1548.         Range rr,effectiveRange;
  1549.         TextStyleRun run;
  1550.         Hashtable attr,newAttr;
  1551.  
  1552.         effectiveRange = allocateRange();
  1553.         for(i=0,c=runVector.count();i<c;i++) {
  1554.             rr = (Range)runVector.elementAt(i);
  1555.             run = _runForIndex( rr.index);
  1556.             attr = run.attributes();
  1557.             if( attr != null && attr.get(attribute) != null) {
  1558.                 newAttr = (Hashtable)attr.clone();
  1559.                 newAttr.remove(attribute);
  1560.                 effectiveRange.index  = r.index;
  1561.                 effectiveRange.length = r.length;
  1562.                 effectiveRange.intersectWith(rr);
  1563.                 setAttributesForRange( newAttr, effectiveRange);
  1564.             }
  1565.         }
  1566.     }
  1567.  
  1568.     /** Adds the attribute <b>attribute</b> with value <b>value</b> to the
  1569.       * default attributes.
  1570.       */
  1571.     public void addDefaultAttribute(String attribute, Object value) {
  1572.         Hashtable defAttr = defaultAttributes();
  1573.  
  1574.         defAttr.put(attribute, value);
  1575.         setDefaultAttributes(defAttr);
  1576.     }
  1577.  
  1578.     /** Decomposes <b>aRange</b> into a Vector of ranges. Each Range represents
  1579.       * a run. The Vector includes the left-most and right-most Ranges, even
  1580.       * if they are not fully included in <b>aRange</b>.
  1581.       */
  1582.     public Vector runsForRange(Range aRange) {
  1583.         TextStyleRun startRun,endRun,run;
  1584.         Vector result = new Vector();
  1585.         Vector runsAfter = null;
  1586.         Vector runs;
  1587.         int i,c,j,d;
  1588.         boolean sameParagraph = false;
  1589.  
  1590.         if( aRange.length == 0 ) {
  1591.             Range r = runForIndex(aRange.index);
  1592.             if( !r.isNullRange() )
  1593.                 result.addElement(r);
  1594.             return result;
  1595.         }
  1596.         startRun = _runForIndex(aRange.index);
  1597.         if( startRun == null )
  1598.             startRun = _runForIndex(0);
  1599.  
  1600.         endRun   = _runForIndex(aRange.index + aRange.length() -1);
  1601.         if( endRun  == null )
  1602.             endRun = _runForIndex(length()-1);
  1603.  
  1604.         runs = startRun.paragraph().runVector();
  1605.  
  1606.         if( startRun.paragraph() == endRun.paragraph() ) {
  1607.             c  = runs.indexOfIdentical(endRun) + 1;
  1608.             sameParagraph=true;
  1609.         } else {
  1610.             c  = runs.count();
  1611.             sameParagraph=false;
  1612.         }
  1613.  
  1614.         for(i=runs.indexOfIdentical(startRun) ; i < c ; i++ )
  1615.             result.addElement( ((TextStyleRun)runs.elementAt(i)).range());
  1616.  
  1617.         if(!  sameParagraph ) {
  1618.             for( i = _paragraphVector.indexOfIdentical( startRun.paragraph() ) + 1 ,
  1619.                      c = _paragraphVector.indexOfIdentical( endRun.paragraph() ) ; i < c ; i++ ) {
  1620.                 runs = ((TextParagraph)_paragraphVector.elementAt( i )).runVector();
  1621.                 for(j=0, d = runs.count() ;  j < d ; j++ )
  1622.                     result.addElement( ((TextStyleRun)runs.elementAt(j)).range() );
  1623.             }
  1624.  
  1625.             runs = endRun.paragraph().runVector();
  1626.             for( i = 0 , c = runs.indexOfIdentical(endRun) ; i <= c ; i++ )
  1627.                 result.addElement(((TextStyleRun)runs.elementAt(i)).range());
  1628.         }
  1629.         return result;
  1630.     }
  1631.  
  1632.     /** Decomposes <b>aRange</b> into a Vector of Ranges, with each Range
  1633.       * representing a paragraph.  The Vector includes the left-most and
  1634.       * right-most Ranges, even if they are not fully included in
  1635.       * <b>aRange</b>.
  1636.       */
  1637.     public Vector paragraphsForRange(Range aRange) {
  1638.         Range pr = paragraphsRangeForRange(aRange);
  1639.         int i,c;
  1640.         Vector result = new Vector();
  1641.         Range r;
  1642.  
  1643.         for(i=pr.index,c=pr.index+pr.length ; i < c ; i++ ) {
  1644.             r = ((TextParagraph)_paragraphVector.elementAt(i)).range();
  1645.             if( i == (c - 1)) { /* Last paragraph */
  1646.                 r.length--; /* Remove the last \n that does not exist */
  1647.             }
  1648.             result.addElement(r);
  1649.         }
  1650.         return result;
  1651.     }
  1652.  
  1653.     /** Returns the run Range to which the character at <b>anIndex</b> belongs.
  1654.       */
  1655.     public Range runForIndex(int anIndex) {
  1656.         TextStyleRun run = _runForIndex(anIndex);
  1657.         if( run == null )
  1658.             return allocateRange();
  1659.         else
  1660.             return run.range();
  1661.     }
  1662.  
  1663.     /** Returns the paragraph Range to which the character at <b>anIndex</b>
  1664.       * belongs.
  1665.       */
  1666.     public Range paragraphForIndex(int anIndex) {
  1667.         Vector v;
  1668.         Range pr = allocateRange(anIndex,0);
  1669.         v = paragraphsForRange(pr);
  1670.         recycleRange(pr);
  1671.         if( v.count() > 0 )
  1672.             return (Range)v.elementAt(0);
  1673.         else
  1674.             return allocateRange();
  1675.     }
  1676.  
  1677.     /** Returns the Range of the paragraph containing the point
  1678.       * (<b>x</b>, <b>y</b>).
  1679.       */
  1680.     public Range paragraphForPoint(int x,int y) {
  1681.         TextParagraph p = _paragraphForPoint(x,y);
  1682.         if( p != null )
  1683.             return p.range();
  1684.         else
  1685.             return allocateRange(); /* Return a nullRange() */
  1686.     }
  1687.  
  1688.     /** Returns the Range of the run containing the point
  1689.       * (<b>x</b>, <b>y</b>).
  1690.       */
  1691.     public Range runForPoint(int x,int y) {
  1692.         TextPositionInfo info = positionForPoint( x,y,true );
  1693.         TextStyleRun run;
  1694.  
  1695.         if( info != null ) {
  1696.             run = _runForIndex( info._absPosition );
  1697.             if( run != null )
  1698.                 return run.range();
  1699.         }
  1700.         return allocateRange(); /* return a nullRange() */
  1701.     }
  1702.  
  1703.     /** Returns the index of the character under the point
  1704.       * (<b>x</b>, <b>y</b>).
  1705.       *  Returns -1 if there is no character under (<b>x</b>, <b>y</b>).
  1706.       */
  1707.     public int   indexForPoint(int x,int y) {
  1708.         TextPositionInfo info = positionForPoint(x,y,true);
  1709.         if( info != null )
  1710.             return info._absPosition;
  1711.         else
  1712.             return -1;
  1713.     }
  1714.  
  1715.     /** Returns the Vector of Rects enclosing <b>aRange</b>.
  1716.       * Rectangles are ordered from top to bottom.
  1717.       * If the Range length is null or the Range is completely out of bounds,
  1718.       * this method returns an empty Vector.  If the Range is partially out of
  1719.       * bounds, this method returns the Rects for the intersection
  1720.       * of the given Range and the Range of the text.
  1721.       */
  1722.     public Vector rectsForRange(Range aRange) {
  1723.         return rectsForRange(aRange,null);
  1724.     }
  1725.  
  1726.  
  1727.     /*
  1728.      *  Selection
  1729.      */
  1730.  
  1731.     /** Returns the selected Range. Returns Range.nullRange() if no selection
  1732.       * exists.
  1733.       */
  1734.     public Range selectedRange() {
  1735.  
  1736.         if( _selectedRange == null )
  1737.             _selectedRange = allocateRange();
  1738.  
  1739.         _selectedRange.index = _selection.selectionStart();
  1740.         if( _selectedRange.index < 0 )
  1741.             return allocateRange();
  1742.         _selectedRange.length = _selection.selectionEnd() -
  1743.                                 _selection.selectionStart();
  1744.         return _selectedRange;
  1745.     }
  1746.  
  1747.     /** Sets the selected Range. If <b>aRange</b> is Range.nullRange(),
  1748.       * removes the TextView's selection.
  1749.       */
  1750.     public void selectRange(Range aRange) {
  1751.         /* No need to refresh since _selection.setRange does it */
  1752.         if( aRange.isNullRange() ) {
  1753.             _selection.clearRange();
  1754.         } else {
  1755.             _selection.setRange(aRange.index(), aRange.lastIndex()+1);
  1756.         }
  1757.  
  1758.         _selectionChanged();
  1759.     }
  1760.  
  1761.     /** Returns <b>true</b> if the TextView has a selection. */
  1762.     public boolean hasSelection() {
  1763.         Range r = selectedRange();
  1764.         if(r.isNullRange())
  1765.             return false;
  1766.         else
  1767.             return true;
  1768.     }
  1769.  
  1770.  
  1771.     /* Import */
  1772.  
  1773.     /** Primitive to insert some HTML elements into a range. Vector should
  1774.       * contain some TextViewHTMLElement. This method is useful when
  1775.       * extending HTML parsing with some containers producing TextAttachment.
  1776.       * Use importHTMLInRange() or importHTMLFromURLString() in any other
  1777.       * <b>attributes</b> is the attributes that should be used as base to
  1778.       * parse HTML. For example if you set FONT_KEY to be helvetica 18,
  1779.       * all HTML text will be helvetica 18, but if HTML changes the font.
  1780.       *
  1781.       */
  1782.     public void insertHTMLElementsInRange(Vector components, Range aRange,Hashtable attributes) {
  1783.         String s;
  1784.         int i,c;
  1785.         int lengths[] = new int[components.count()];
  1786.         int offset=0,length=0;
  1787.         Hashtable context = new Hashtable();
  1788.         FastStringBuffer fb = new FastStringBuffer();
  1789.         Hashtable initialAttributes;
  1790.  
  1791.         if( attributes == null )
  1792.             attributes = defaultAttributes();
  1793.  
  1794.         initialAttributes = TextView.attributesByRemovingStaticAttributes(attributes);
  1795.  
  1796.         setDirty(true); /* Set dirty now to avoid computing dirty rects during parsing */
  1797.         disableResizing();
  1798.         fb.setDoublesCapacityWhenGrowing(true);
  1799.  
  1800.         for(i=0,c = components.count() ; i < c  ; i++) {
  1801.             ((TextViewHTMLElement)components.elementAt(i)).appendString(context,fb);
  1802.             if( i == 0 )
  1803.                 length = lengths[0] = fb.length();
  1804.             else {
  1805.                 lengths[i] = fb.length() - length;
  1806.                 length += lengths[i];
  1807.             }
  1808.         }
  1809.  
  1810.         this.replaceRangeWithString(aRange, fb.toString());
  1811.  
  1812.         disableFormatting();
  1813.  
  1814.         for(i=0,c = components.count() ; i < c ; i++ ) {
  1815.             ((TextViewHTMLElement)components.elementAt(i)).setAttributesStartingAt(
  1816.                 aRange.index + offset , initialAttributes,this,context);
  1817.             offset += lengths[i];
  1818.         }
  1819.  
  1820.         enableFormatting();
  1821.         reformatAll();
  1822.         enableResizing();
  1823.         sizeToMinSize();
  1824.     }
  1825.  
  1826.     /** Convenience to import HTML from the stream <b>inputStream</b>,
  1827.       * replacing the text defined by <b>aRange</b>.  If <b>baseURL</b> is
  1828.       * <b>null</b>, only absolute HTTP references will work.<p>
  1829.       * Default attributes will be used as a base to parse HTML.
  1830.       * Supports HTML v1.0.
  1831.       */
  1832.     public void importHTMLInRange(InputStream inputStream, Range aRange,
  1833.                                   URL baseURL) throws IOException, HTMLParsingException {
  1834.         importHTMLInRange(inputStream,aRange,baseURL,defaultAttributes());
  1835.     }
  1836.  
  1837.     /** Imports HTML from the stream <b>inputStream</b>, replacing the text
  1838.       * defined by <b>aRange</b>.  If <b>baseURL</b> is <b>null</b>, only
  1839.       * absolute HTTP references.<p>
  1840.       * <b>attributes</b> is the attributes that should be used as base to
  1841.       * parse HTML. For example if you set FONT_KEY to be helvetica 18,
  1842.       * all HTML text will be helvetica 18, but if HTML changes the font.
  1843.       * Supports HTML v1.0.
  1844.       */
  1845.     public void importHTMLInRange(InputStream inputStream, Range aRange,
  1846.                                   URL baseURL,Hashtable attributes)
  1847.         throws IOException, HTMLParsingException {
  1848.         Vector components = new Vector();
  1849.         HTMLParser parser;
  1850.         HTMLElement component;
  1851.         int i,c;
  1852.  
  1853.  
  1854.         validateHTMLParsingRules();
  1855.         parser = new HTMLParser(inputStream,_htmlParsingRules);
  1856.  
  1857.  
  1858.         try {
  1859.             while((component = parser.nextHTMLElement()) != null)
  1860.                 components.addElement(component);
  1861.         } catch( java.lang.InstantiationException e) {
  1862.             throw new InconsistencyException("Cannot intantiate HTML storage");
  1863.         } catch (java.lang.IllegalAccessException e) {
  1864.             throw new InconsistencyException("Cannot access HTML storage classes");
  1865.         }
  1866.  
  1867.         setBaseURL( baseURL );
  1868.         insertHTMLElementsInRange(components, aRange,attributes);
  1869.     }
  1870.  
  1871.     /** Imports HTML from the URL <b>urlString</b>.
  1872.       * Scrolls the TextView to the top and sets the selection to (0, 0).
  1873.      */
  1874.     public void importHTMLFromURLString(String urlString) {
  1875.         Range r;
  1876.         URL url;
  1877.         InputStream in;
  1878.         r = allocateRange(0,length());
  1879.         try {
  1880.             url = new URL(urlString);
  1881.             in  = url.openStream();
  1882.             this.importHTMLInRange( in , r , url,defaultAttributes());
  1883.             r.index = 0;
  1884.             r.length = 0;
  1885.             this.selectRange(r);
  1886.             this.scrollRangeToVisible(r);
  1887.         } catch (MalformedURLException e) {
  1888.             System.err.println("Bad URL " + urlString );
  1889.         } catch (IOException e) {
  1890.             System.err.println("IOException while reading " + urlString);
  1891.         } catch (HTMLParsingException e ) {
  1892.             System.err.println("At line " + e.lineNumber() + ":" + e );
  1893.         }
  1894.         recycleRange( r );
  1895.     }
  1896.  
  1897.     /** Set the HTML parsing rules
  1898.       *
  1899.       */
  1900.     public void setHTMLParsingRules(HTMLParsingRules newRules) {
  1901.         _htmlParsingRules = newRules;
  1902.     }
  1903.  
  1904.     /** Return the HTML parsing rules
  1905.       *
  1906.       */
  1907.     public HTMLParsingRules htmlParsingRules() {
  1908.         validateHTMLParsingRules();
  1909.         return _htmlParsingRules;
  1910.     }
  1911.  
  1912.     /** Returns the Range of the run with the attribute
  1913.       * <b>LINK_DESTINATION_KEY</b> set to <b>aName</b>.
  1914.       * Returns a null Range if not found. Use <B>scrollRangeToVisible()</B>
  1915.       * to scroll the TextView to make the link destination visible.
  1916.       */
  1917.     public Range runWithLinkDestinationNamed(String aName) {
  1918.         int i,c;
  1919.         int j,d;
  1920.         TextParagraph paragraph;
  1921.         TextStyleRun run=null;
  1922.         String name;
  1923.         Range result = null;
  1924.  
  1925.         for(i = 0, c = _paragraphVector.count() ; i < c ; i++ ) {
  1926.             paragraph = (TextParagraph) _paragraphVector.elementAt(i);
  1927.             for(j=0,d = paragraph._runVector.count() ; j < d ; j++ ) {
  1928.                 run = (TextStyleRun) paragraph._runVector.elementAt(j);
  1929.                 if( run._attributes != null &&
  1930.                     (name = (String)run._attributes.get(LINK_DESTINATION_KEY)) != null ) {
  1931.                     if( name.equals(aName)) {
  1932.                         result = run.range();
  1933.                         break;
  1934.                     }
  1935.                 }
  1936.             }
  1937.             if(result != null)
  1938.                 break;
  1939.         }
  1940.  
  1941.         if(result == null)
  1942.             return new Range();
  1943.         else if(run != null) {
  1944.             Range initialResult = result;
  1945.             while(result.length == 0) {
  1946.                 run = runAfter(run);
  1947.                 if(run != null)
  1948.                     result = run.range();
  1949.                 else {
  1950.                     result = initialResult;
  1951.                     break;
  1952.                 }
  1953.             }
  1954.         }
  1955.         return result;
  1956.     }
  1957.  
  1958.     /* archiving */
  1959.  
  1960.  
  1961.     /** Describes the TextView class' information.
  1962.       * @see Codable#describeClassInfo
  1963.       */
  1964.     public void describeClassInfo(ClassInfo info) {
  1965.         super.describeClassInfo(info);
  1966.  
  1967.         info.addClass("netscape.application.TextView", 2);
  1968.         info.addField(PARAGRAPHVECTOR_KEY, OBJECT_TYPE);
  1969.         info.addField(BACKGROUNDCOLOR_KEY, OBJECT_TYPE);
  1970.         info.addField(SELECTIONCOLOR_KEY, OBJECT_TYPE);
  1971.         info.addField(FILTER_KEY, OBJECT_TYPE);
  1972.         info.addField(DEFAULTATTRIBUTES_KEY, OBJECT_TYPE);
  1973.         info.addField(PARASPACING_KEY, INT_TYPE);
  1974.         info.addField(USESINGLEFONT_KEY, BOOLEAN_TYPE);
  1975.         info.addField(EDITABLE_KEY, BOOLEAN_TYPE);
  1976.         info.addField(SELECTABLE_KEY, BOOLEAN_TYPE);
  1977.         info.addField(TRANSPARENT_KEY, BOOLEAN_TYPE);
  1978.         info.addField(HTML_PARSING_RULES_KEY,OBJECT_TYPE);
  1979.         info.addField(OWNER_KEY,OBJECT_TYPE);
  1980.     }
  1981.  
  1982.     /** Encodes the TextView instance.
  1983.       * @see Codable#encode
  1984.       */
  1985.     public void encode(Encoder encoder) throws CodingException {
  1986.         super.encode(encoder);
  1987.  
  1988.         encoder.encodeObject(PARAGRAPHVECTOR_KEY, _paragraphVector);
  1989.         encoder.encodeObject(BACKGROUNDCOLOR_KEY, _backgroundColor);
  1990.         encoder.encodeObject(SELECTIONCOLOR_KEY, _selectionColor);
  1991.         encoder.encodeObject(FILTER_KEY, (Codable)_filter);
  1992.         encoder.encodeObject(DEFAULTATTRIBUTES_KEY, _defaultAttributes);
  1993.         encoder.encodeInt(PARASPACING_KEY, _paragraphSpacing);
  1994.         encoder.encodeBoolean(USESINGLEFONT_KEY, _useSingleFont);
  1995.         encoder.encodeBoolean(EDITABLE_KEY, _editable);
  1996.         encoder.encodeBoolean(SELECTABLE_KEY, _selectable);
  1997.         encoder.encodeBoolean(TRANSPARENT_KEY, transparent);
  1998.         encoder.encodeObject(HTML_PARSING_RULES_KEY,_htmlParsingRules);
  1999.         encoder.encodeObject(OWNER_KEY,_owner);
  2000.     }
  2001.  
  2002.     /** Decodes the TextView instance.
  2003.       * @see Codable#decode
  2004.       */
  2005.     public void decode(Decoder decoder) throws CodingException {
  2006.         int version = decoder.versionForClassName("netscape.application.TextView");
  2007.  
  2008.         super.decode(decoder);
  2009.  
  2010.         _paragraphVector = (Vector)decoder.decodeObject(PARAGRAPHVECTOR_KEY);
  2011.         _backgroundColor = (Color)decoder.decodeObject(BACKGROUNDCOLOR_KEY);
  2012.         _selectionColor = (Color)decoder.decodeObject(SELECTIONCOLOR_KEY);
  2013.         _filter = (TextFilter)decoder.decodeObject(FILTER_KEY);
  2014.         _defaultAttributes = (Hashtable)decoder.decodeObject(DEFAULTATTRIBUTES_KEY);
  2015.         _paragraphSpacing = decoder.decodeInt(PARASPACING_KEY);
  2016.         _useSingleFont = decoder.decodeBoolean(USESINGLEFONT_KEY);
  2017.         _editable = decoder.decodeBoolean(EDITABLE_KEY);
  2018.         _selectable = decoder.decodeBoolean(SELECTABLE_KEY);
  2019.         transparent = decoder.decodeBoolean(TRANSPARENT_KEY);
  2020.  
  2021.         if(version >= 2) {
  2022.             _htmlParsingRules = (HTMLParsingRules) decoder.decodeObject(HTML_PARSING_RULES_KEY);
  2023.             _owner = (TextViewOwner) decoder.decodeObject(OWNER_KEY);
  2024.         }
  2025.     }
  2026.  
  2027.     /** Finishes the TextView instance decoding.
  2028.       * @see Codable#finishDecoding
  2029.       */
  2030.     public void finishDecoding() throws CodingException {
  2031.         TextParagraph   nextParagraph;
  2032.         int             i;
  2033.  
  2034.         super.finishDecoding();
  2035.  
  2036.         // Drop the font metrics cache
  2037.         _defaultFontMetricsCache = null;
  2038.  
  2039.         i = _paragraphVector.count();
  2040.         while (i-- > 0) {
  2041.             nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  2042.             nextParagraph.setOwner(this);
  2043.         }
  2044.  
  2045.         reformatAll();
  2046.     }
  2047.  
  2048.     /** Return the number of lines currently in TextView.
  2049.       *
  2050.       */
  2051.     public int lineCount() {
  2052.         int i,c,result = 0;
  2053.         TextParagraph p;
  2054.  
  2055.         for(i=0,c=_paragraphVector.count(); i < c ; i++ ) {
  2056.             p = (TextParagraph)_paragraphVector.elementAt(i);
  2057.             result += p.lineCount();
  2058.         }
  2059.  
  2060.         return result;
  2061.     }
  2062.  
  2063.     /** Set the base URL of the displayed document.**/
  2064.     public void setBaseURL(URL baseURL) {
  2065.         _baseURL = baseURL;
  2066.     }
  2067.  
  2068.  
  2069.     /** Return the baseURL for the document. The return value is defined
  2070.       * only while parsing some HTML, or when the value has been set
  2071.       * with setBaseURL()
  2072.       *
  2073.       */
  2074.     public URL baseURL() {
  2075.         return _baseURL;
  2076.     }
  2077.  
  2078.  
  2079.     /** Return whether this view can become the selected view
  2080.       * when the user is moving from view to views with the keyboard
  2081.       * The default implementation returns <b>true</b> if the TextView
  2082.       * is selectable.
  2083.       *
  2084.      */
  2085.     public boolean canBecomeSelectedView() {
  2086.         if( isEditable() || isSelectable())
  2087.             return true;
  2088.         else
  2089.             return false;
  2090.     }
  2091.  
  2092.     /** Inform the view that it is about to become the selected view.
  2093.       *
  2094.       */
  2095.     public void willBecomeSelected() {
  2096.         setFocusedView();
  2097.     }
  2098.  
  2099.     /** Converts CR/LF to CR. */
  2100.     public static String stringWithoutCarriageReturns(String aString) {
  2101.         FastStringBuffer sb = new FastStringBuffer();
  2102.         int i,c;
  2103.         char ch;
  2104.  
  2105.         for(i=0,c=aString.length(); i < c ; i++ ) {
  2106.             ch = aString.charAt(i);
  2107.             if( ch == '\r' && (i+1) < c && aString.charAt(i+1) == '\n')
  2108.                 continue;
  2109.             else
  2110.                 sb.append(ch);
  2111.         }
  2112.         return sb.toString();
  2113.     }
  2114.  
  2115.  
  2116.     /**
  2117.          * Return and cache the fontMetrics for the default font
  2118.          */
  2119.     FontMetrics defaultFontMetrics() {
  2120.         if( _defaultFontMetricsCache == null )
  2121.             _defaultFontMetricsCache = font().fontMetrics();
  2122.         return _defaultFontMetricsCache;
  2123.     }
  2124.  
  2125.  
  2126.     /* Static conveniences */
  2127.     static Hashtable attributesByRemovingStaticAttributes(Hashtable attributes) {
  2128.         if( attributes == null )
  2129.             return null;
  2130.         else {
  2131.             Hashtable h = (Hashtable) attributes.clone();
  2132.             h.remove(TEXT_ATTACHMENT_KEY);
  2133.             h.remove(TEXT_ATTACHMENT_BASELINE_OFFSET_KEY);
  2134.             return h;
  2135.         }
  2136.     }
  2137.  
  2138.     /** Adds <b>aParagraph</b> to the TextView.  You must call
  2139.          * <b>reformatAll()</b> * to get the TextView to properly reformat itself.
  2140.          * @see #reformat
  2141.          */
  2142.     private void addParagraph(TextParagraph aParagraph) {
  2143.         if (aParagraph == null) {
  2144.             return;
  2145.         }
  2146.  
  2147.         aParagraph.setOwner(this);
  2148.         _paragraphVector.addElement(aParagraph);
  2149.     }
  2150.  
  2151.     /* Set whether this text view can edit. The textview can edit if
  2152.      * it has the focus and a selection
  2153.      */
  2154.     synchronized void _setEditing(boolean flag) {
  2155.         _editing = flag;
  2156.     }
  2157.  
  2158.     synchronized boolean isEditing() {
  2159.         return _editing;
  2160.     }
  2161.  
  2162.     private void reformatAll() {
  2163.         TextParagraph   nextParagraph;
  2164.         int             currentHeight, i, count;
  2165.         Range wasSelectedRange = selectedRange();
  2166.         currentHeight = 0;
  2167.         _charCount = 0;
  2168.         count = _paragraphVector.count();
  2169.  
  2170.         if( ! formattingEnabled() )
  2171.             return;
  2172.         for (i = 0; i < count; i++) {
  2173.             nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  2174.             nextParagraph._y = currentHeight;
  2175.             nextParagraph._startChar = _charCount;
  2176.             nextParagraph.computeLineBreaksAndHeights(bounds.width);
  2177.             currentHeight += nextParagraph._height + _paragraphSpacing;
  2178.             _charCount += nextParagraph._charCount;
  2179.         }
  2180.  
  2181.         sizeBy(0, currentHeight - bounds.height);
  2182.         notifyAttachmentsForRange(new Range(0,length()),true);
  2183.         selectRange( wasSelectedRange );
  2184.     }
  2185.  
  2186.     /** Disables TextView paragraph formatting.  If you are about to perform several
  2187.          * changes to the TextView attributes, call this method to disable the auto-formatting,
  2188.          * to avoid recomputing the paragraph layout.
  2189.          * @see #enableFormatting()
  2190.          */
  2191.     void disableFormatting() {
  2192.         _formattingDisabled++;
  2193.     }
  2194.  
  2195.     /** Reenables TextView formatting.  You should call <b>reformat()</b>
  2196.          * to get the TextView to relayout its contents.
  2197.          *  @see #disableFormatting
  2198.          */
  2199.     void enableFormatting() {
  2200.         _formattingDisabled--;
  2201.         if (_formattingDisabled < 0) {
  2202.             _formattingDisabled = 0;
  2203.         }
  2204.     }
  2205.  
  2206.     /** Returns <b>true</b> if formatting is enabled.
  2207.          * @see #disableFormatting
  2208.          */
  2209.     boolean formattingEnabled() {
  2210.         return _formattingDisabled == 0;
  2211.     }
  2212.  
  2213.  
  2214.     private void formatParagraphAtIndex(int pIndex) {
  2215.       TextParagraph   nextParagraph,aParagraph;
  2216.         int             index, count, i, currentHeight;
  2217.         Range changedRange = new Range();
  2218.  
  2219.         if( ! formattingEnabled() )
  2220.             return;
  2221.  
  2222.         if(pIndex == -1)
  2223.           return;
  2224.         aParagraph = (TextParagraph) _paragraphVector.elementAt(pIndex);
  2225.         index = pIndex - 1;
  2226.         if (index < -1) {
  2227.             return;
  2228.         } else if (index == -1) {
  2229.             _charCount = 0;
  2230.             currentHeight = 0;
  2231.         } else {
  2232.             nextParagraph = (TextParagraph)_paragraphVector.elementAt(index);
  2233.             _charCount = nextParagraph._startChar + nextParagraph._charCount;
  2234.             currentHeight = nextParagraph._y + nextParagraph._height +
  2235.                 _paragraphSpacing;
  2236.         }
  2237.  
  2238.  
  2239.         aParagraph.setY(currentHeight);
  2240.         aParagraph.setStartChar(_charCount);
  2241.         aParagraph.computeLineBreaksAndHeights(bounds.width);
  2242.  
  2243.         _charCount += aParagraph._charCount;
  2244.         currentHeight += aParagraph._height + _paragraphSpacing;
  2245.  
  2246.         /* reposition paragraphs below this one */
  2247.         count = _paragraphVector.count();
  2248.         for (i = pIndex + 1; i < count; i++) {
  2249.             nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  2250.             nextParagraph.setY(currentHeight);
  2251.             nextParagraph.setStartChar(_charCount);
  2252.             currentHeight += nextParagraph._height + _paragraphSpacing;
  2253.             _charCount += nextParagraph._charCount;
  2254.         }
  2255.  
  2256.         changedRange.index = aParagraph._startChar;
  2257.         changedRange.unionWith(lastParagraph().range());
  2258.         sizeBy(0, currentHeight - bounds.height);
  2259.         notifyAttachmentsForRange(changedRange,true);
  2260.     }
  2261.  
  2262.     private void formatParagraph(TextParagraph aParagraph) {
  2263.       formatParagraphAtIndex(_paragraphVector.indexOfIdentical(aParagraph));
  2264.     }
  2265.  
  2266.     private int adjustCharCountsAndSpacing(int pIndex) {
  2267.         TextParagraph   nextParagraph;
  2268.         int             currentHeight, i, count;
  2269.         currentHeight = _charCount = 0;
  2270.  
  2271.         if(pIndex > 0) {
  2272.           nextParagraph = (TextParagraph)_paragraphVector.elementAt(pIndex - 1);
  2273.           currentHeight = nextParagraph._y + nextParagraph._height + _paragraphSpacing;
  2274.           _charCount    = nextParagraph._startChar + nextParagraph._charCount;
  2275.         }
  2276.  
  2277.         count = _paragraphVector.count();
  2278.         for (i = pIndex; i < count; i++) {
  2279.             nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  2280.             nextParagraph._y = currentHeight;
  2281.             nextParagraph._startChar = _charCount;
  2282.             currentHeight += nextParagraph._height + _paragraphSpacing;
  2283.             _charCount += nextParagraph._charCount;
  2284.         }
  2285.  
  2286.         return currentHeight;
  2287.     }
  2288.  
  2289.     private int adjustCharCountsAndSpacing() {
  2290.       return adjustCharCountsAndSpacing(0);
  2291.     }
  2292.  
  2293.     int _paragraphIndexForIndex(int absPosition) {
  2294.         TextParagraph   nextParagraph;
  2295.         int             count, i;
  2296.  
  2297.         count = _paragraphVector.count();
  2298.         if(absPosition > (length() / 2)) {
  2299.             for (i = count-1; i >= 0; i--) {
  2300.                 nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  2301.  
  2302.                 if (absPosition >= nextParagraph._startChar &&
  2303.                     absPosition < nextParagraph._startChar +
  2304.                     nextParagraph._charCount) {
  2305.                   return i;
  2306.                 }
  2307.             }
  2308.         } else {
  2309.             for (i = 0; i < count; i++) {
  2310.                 nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  2311.  
  2312.                 if (absPosition >= nextParagraph._startChar &&
  2313.                     absPosition < nextParagraph._startChar +
  2314.                     nextParagraph._charCount) {
  2315.                   return i;
  2316.                 }
  2317.             }
  2318.         }
  2319.  
  2320.     return -1;
  2321.   }
  2322.  
  2323.     TextParagraph _paragraphForIndex(int absPosition) {
  2324.       int p = _paragraphIndexForIndex(absPosition);
  2325.       if(p!=-1)
  2326.         return (TextParagraph)_paragraphVector.elementAt(p);
  2327.       else
  2328.         return null;
  2329.     }
  2330.  
  2331.     private TextParagraph _paragraphForPoint(int x, int y) {
  2332.         TextParagraph   nextParagraph;
  2333.         int             count, i, minY, maxY;
  2334.  
  2335.         if (y < 0) {
  2336.             y = 0;
  2337.             x = 0;
  2338.         }
  2339.  
  2340.         count = _paragraphVector.count();
  2341.         minY = 0;
  2342.         for (i = 0; i < count; i++) {
  2343.             nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  2344.  
  2345.             maxY = minY + nextParagraph._height;
  2346.             if (y >= minY && y < maxY) {
  2347.                 return nextParagraph;
  2348.             }
  2349.             minY = maxY;
  2350.         }
  2351.  
  2352.         return null;
  2353.     }
  2354.  
  2355.     TextPositionInfo positionInfoForIndex( int index ) {
  2356.         TextParagraph p = _paragraphForIndex(index);
  2357.         if( p == null )
  2358.             p = lastParagraph();
  2359.         return p._infoForPosition( index );
  2360.     }
  2361.  
  2362.     /* If absolute is false, this method will return the next character
  2363.          * if the hit point is on the right of the glyph center. Otherwise
  2364.          * it will return the character for the glyph.
  2365.          * Example:  Netscape
  2366.          * flag is false, (x,y) is on the 't' a little after the center of the glyph 't'.
  2367.          *     the returned position will be the position of s.
  2368.          * flag is true , (x,y) is on the 't' a little after the center of the glyph 't'.
  2369.          *     the returned position will be the position of t
  2370.          */
  2371.     private TextPositionInfo positionForPoint(int x, int y, boolean absolute) {
  2372.         TextParagraph   theParagraph;
  2373.  
  2374.         if (y < 0) {
  2375.             y = 0;
  2376.             x = 0;
  2377.         }
  2378.  
  2379.         theParagraph = _paragraphForPoint(x, y);
  2380.         if (theParagraph == null) {
  2381.             if (_charCount == 0) {
  2382.                 return lastParagraph().infoForPosition(_charCount, y);
  2383.             }
  2384.             return lastParagraph().infoForPosition(_charCount - 1, y);
  2385.         }
  2386.  
  2387.         return theParagraph.positionForPoint(x, y,absolute);
  2388.     }
  2389.  
  2390.  
  2391.     void drawInsertionPoint() {
  2392.         Rect    tmpRect;
  2393.         tmpRect = _selection.insertionPointRect();
  2394.         addDirtyRect(tmpRect);
  2395.         TextView.returnRect(tmpRect);
  2396.     }
  2397.  
  2398.     private void insertString(String aString,int insertionPoint) {
  2399.         insertString(aString,insertionPoint,false);
  2400.     }
  2401.  
  2402.     private void insertString(String aString,int insertionPoint,boolean formatParagraph) {
  2403.         TextParagraph           editParagraph, lastParagraph, oldUpdate;
  2404.         TextPositionInfo        newPosition, insertionPointInfo;
  2405.         Rect                    updateRect;
  2406.         int                     oldHeight, newHeight, oldLine,
  2407.                                 insertCount = 0;
  2408.         Range r;
  2409.         TextParagraphFormat     format;
  2410.         int paragraphIndex;
  2411.  
  2412.         if (aString == null) {
  2413.             return;
  2414.         }
  2415.  
  2416.         paragraphIndex = _paragraphIndexForIndex(insertionPoint);
  2417.         if(paragraphIndex == -1)
  2418.           return;
  2419.         editParagraph = (TextParagraph) _paragraphVector.elementAt(paragraphIndex);
  2420.  
  2421.         if (editParagraph == null) {
  2422.             return;
  2423.         }
  2424.  
  2425.         lastParagraph = lastParagraph();
  2426.         if (lastParagraph != null) {
  2427.             oldHeight = lastParagraph._y + lastParagraph._height;
  2428.         } else {
  2429.             oldHeight = 0;
  2430.         }
  2431.  
  2432.         insertionPointInfo = positionInfoForIndex( insertionPoint );
  2433.         if (insertionPointInfo == null) {
  2434.             insertionPointInfo = editParagraph.infoForPosition(
  2435.                                                                insertionPoint, -1);
  2436.         }
  2437.  
  2438.         newPosition = editParagraph.insertCharOrStringAt('\0', aString, insertionPoint);
  2439.         insertCount = aString.length();
  2440.         _charCount += insertCount;
  2441.  
  2442.         if (newPosition == null) { /** Nothing actually happened **/
  2443.             _selection.showInsertionPoint();
  2444.             return;
  2445.         }
  2446.  
  2447.         if( isDirty() && dirtyRect == null ) {
  2448.             r = new Range(editParagraph._startChar, length() - editParagraph._startChar);
  2449.             notifyAttachmentsForRange(r,true);
  2450.             return;
  2451.         }
  2452.  
  2453.  
  2454.         if(formatParagraph)
  2455.             formatParagraphAtIndex(paragraphIndex);
  2456.  
  2457.         newPosition.setAbsPosition(insertionPoint + insertCount);
  2458.         format = editParagraph.currentParagraphFormat();
  2459.  
  2460.         if (newPosition._redrawCurrentLineOnly && format._justification == Graphics.LEFT_JUSTIFIED) {
  2461.             //System.out.println("Redraw current line only");
  2462.             oldUpdate = _updateParagraph;
  2463.             oldLine = _updateLine;
  2464.  
  2465.             _updateParagraph = editParagraph;
  2466.             _updateLine = newPosition._updateLine;
  2467.             _drawText = false;
  2468.             updateRect = editParagraph.rectForLine(newPosition._lineNumber);
  2469.             updateRect.setBounds(insertionPointInfo._x, updateRect.y,
  2470.                                  updateRect.width - (insertionPointInfo._x - updateRect.x),
  2471.                                  updateRect.height);
  2472.             this.addDirtyRect(updateRect);
  2473.             TextView.returnRect(updateRect);
  2474.             _drawText = true;
  2475.             _updateParagraph = oldUpdate;
  2476.             _updateLine = oldLine;
  2477.             notifyAttachmentsForRange(newPosition.lineRange(),true);
  2478.         } else if (newPosition._redrawCurrentParagraphOnly ||
  2479.                    newPosition._redrawCurrentLineOnly ) {
  2480.             //System.out.println("Redraw paragraph");
  2481.             r = editParagraph.range();
  2482.             notifyAttachmentsForRange(r,true);
  2483.             dirtyRange(r);
  2484.         } else {
  2485.             //System.out.println("Redraw until the end");
  2486.             r = new Range(editParagraph._startChar, length() - editParagraph._startChar);
  2487.             notifyAttachmentsForRange(r,true);
  2488.             dirtyRange(r);
  2489.         }
  2490.  
  2491.     }
  2492.  
  2493.     /** ALERT: This is one of the only remaining method that does not use
  2494.       * replaceRangeWithString() the reason is that replaceRangeWithString does
  2495.       * not know how to refresh exactly one line when possible.
  2496.       */
  2497.     private void fastDeleteChar(boolean after) {
  2498.         TextParagraph           editParagraph, lastParagraph, nextParagraph,
  2499.             previousParagraph, oldUpdate;
  2500.         TextPositionInfo        newPosition, insertionPointInfo;
  2501.         KeyEvent                nextKey;
  2502.         Rect                    updateRect;
  2503.         String                  theString;
  2504.         int                     i, startLine, oldHeight, newHeight, index,
  2505.             insertionPoint, oldLine, insertCount = 0;
  2506.         TextParagraphFormat     format;
  2507.  
  2508.         if (!isEditing()) {
  2509.             //                application().beep();
  2510.             return;
  2511.         }
  2512.  
  2513.  
  2514.         insertionPoint = _selection.insertionPoint();
  2515.         if (!after && insertionPoint == 0 )
  2516.             return;
  2517.  
  2518.         if( after && insertionPoint == length())
  2519.             return;
  2520.  
  2521.         if( after )
  2522.             insertionPoint++; /* We bump insertion point to delete the char after */
  2523.  
  2524.         editParagraph = _paragraphForIndex(insertionPoint);
  2525.  
  2526.         if (editParagraph == null) {
  2527.             return;
  2528.         }
  2529.  
  2530.  
  2531.         notifyAttachmentsForRange(new Range(insertionPoint,1),false);
  2532.         lastParagraph = lastParagraph();
  2533.         if (lastParagraph != null) {
  2534.             oldHeight = lastParagraph._y + lastParagraph._height;
  2535.         } else {
  2536.             oldHeight = 0;
  2537.         }
  2538.  
  2539.         if (editParagraph._startChar == insertionPoint &&
  2540.                !isOnlyParagraph(editParagraph)) {
  2541.             index = _paragraphVector.indexOfIdentical(editParagraph);
  2542.             previousParagraph = (TextParagraph)_paragraphVector.elementAt(
  2543.                                                                 index - 1);
  2544.             if (previousParagraph != null) {
  2545.                 previousParagraph.subsumeParagraph(editParagraph);
  2546.                 _paragraphVector.removeElement(editParagraph);
  2547.                 editParagraph = previousParagraph;
  2548.                 formatParagraph(editParagraph);
  2549.             }
  2550.  
  2551.             if (insertionPoint == 0) {
  2552.                 newPosition = editParagraph.infoForPosition(0, -1);
  2553.             } else {
  2554.                 newPosition = editParagraph.infoForPosition(insertionPoint - 1, -1);
  2555.             }
  2556.         } else {
  2557.             newPosition = editParagraph.removeCharAt(insertionPoint);
  2558.             _charCount--;
  2559.         }
  2560.  
  2561.         insertionPointInfo = newPosition;
  2562.  
  2563.         newHeight = adjustCharCountsAndSpacing();
  2564.  
  2565.         sizeBy(0, newHeight - bounds.height);
  2566.  
  2567.         if (newPosition == null) {
  2568.             _selection.showInsertionPoint();
  2569.             return;
  2570.         }
  2571.  
  2572.         notifyAttachmentsForRange(newPosition._textRun._paragraph.range(),true);
  2573.  
  2574.         if(! after ) {
  2575.             if (insertionPoint == 0) {
  2576.                 newPosition.setAbsPosition(0);
  2577.             } else {
  2578.                 newPosition.setAbsPosition(insertionPoint - 1);
  2579.             }
  2580.             _selection.setInsertionPoint(newPosition);
  2581.             _selectionChanged();
  2582.         } else {
  2583.             /** Selection did not change but we have to do
  2584.              *  this to make sure the caret has the rigth
  2585.              *  size
  2586.              */
  2587.             _selection.setInsertionPoint(newPosition);
  2588.         }
  2589.  
  2590.         format = editParagraph.currentParagraphFormat();
  2591.  
  2592.         if (newPosition._redrawCurrentLineOnly && format._justification == Graphics.LEFT_JUSTIFIED) {
  2593.             oldUpdate = _updateParagraph;
  2594.             oldLine = _updateLine;
  2595.  
  2596.             _updateParagraph = editParagraph;
  2597.             _updateLine = newPosition._updateLine;
  2598.             _drawText = false;
  2599.             updateRect = editParagraph.rectForLine(newPosition._lineNumber);
  2600.             updateRect.setBounds(insertionPointInfo._x, updateRect.y,
  2601.                                  updateRect.width -
  2602.                                  (insertionPointInfo._x - updateRect.x),
  2603.                                  updateRect.height);
  2604.             addDirtyRect(updateRect);
  2605.             TextView.returnRect(updateRect);
  2606.             _drawText = true;
  2607.  
  2608.             _updateParagraph = oldUpdate;
  2609.             _updateLine = oldLine;
  2610.         } else if (newPosition._redrawCurrentParagraphOnly ||
  2611.                    newPosition._redrawCurrentLineOnly ) {
  2612.             dirtyRange( editParagraph.range());
  2613.         } else {
  2614.             Range dirtyRange = allocateRange(editParagraph._startChar, length() - editParagraph._startChar);
  2615.             dirtyRange( dirtyRange );
  2616.             recycleRange( dirtyRange );
  2617.         }
  2618.  
  2619.     }
  2620.  
  2621.     private Rect insertReturn() {
  2622.         Rect result;
  2623.         int insertionPoint;
  2624.         if (_selection.isARange()) {
  2625.             deleteSelection();
  2626.         }
  2627.         insertionPoint = _selection.insertionPoint();
  2628.         result = insertReturn( insertionPoint );
  2629.         _selection.setRange(insertionPoint + 1, insertionPoint + 1);
  2630.         _selectionChanged();
  2631.         return result;
  2632.     }
  2633.  
  2634.     private Rect insertReturn(int insertionPoint) {
  2635.         TextParagraph           editParagraph, oldPara;
  2636.         int                     index;
  2637.  
  2638.         index = _paragraphIndexForIndex(insertionPoint);
  2639.         if(index == -1)
  2640.           index = _paragraphVector.count() - 1;
  2641.         editParagraph = (TextParagraph)_paragraphVector.elementAt(index);
  2642.  
  2643.         oldPara = editParagraph;
  2644.         editParagraph = editParagraph.createNewParagraphAt(insertionPoint);
  2645.         formatParagraphAtIndex(index);
  2646.         _charCount++;
  2647.         _paragraphVector.insertElementAt(editParagraph, index + 1);
  2648.         formatParagraphAtIndex(index + 1);
  2649.         return TextView.newRect(0, oldPara._y, bounds.width,
  2650.                                 bounds.height - oldPara._y);
  2651.     }
  2652.  
  2653.  
  2654.     private TextStyleRun _runForIndex( int index ) {
  2655.         int length = length();
  2656.         if( length > 0 && index >= length )
  2657.             return _runForIndex( length - 1);
  2658.  
  2659.         if( index >= 0 ) {
  2660.             TextParagraph p = _paragraphForIndex(index);
  2661.             if( p != null )
  2662.                 return p.runForCharPosition(index);
  2663.             else {
  2664.                 return null;
  2665.             }
  2666.         } else
  2667.             return null;
  2668.     }
  2669.  
  2670.  
  2671.     private boolean equalsAttributesHint(Hashtable a1,Hashtable a2) {
  2672.         if( a1 == a2 )
  2673.             return true;
  2674.         else if( a1 == null || a2 == null )
  2675.             return false;
  2676.         else {
  2677.             return false; /* This is just a hint. May want to implement more for performance purpose */
  2678.         }
  2679.     }
  2680.  
  2681.     private void _keyDown() {
  2682.         TextParagraph           editParagraph, oldPara;
  2683.         TextPositionInfo        newPosition, currentPosition;
  2684.         Rect                    updateRect, tmpRect;
  2685.         KeyEvent                event;
  2686.         Event                   revEvent;
  2687.         int                     insertionPoint, index,newPos;
  2688.         boolean                 scrollToVisible;
  2689.  
  2690.         revEvent = (Event)_eventVector.removeFirstElement();
  2691.  
  2692.         if (!(revEvent instanceof KeyEvent)) {
  2693.             return;
  2694.         }
  2695.         event = (KeyEvent)revEvent;
  2696.         if(event.key == 1022) /** Hack to workaround awt bug with caps lock key */
  2697.             return;
  2698.         _selection.disableInsertionPoint();
  2699.  
  2700.         insertionPoint = _selection.insertionPoint();
  2701.         editParagraph = _paragraphForIndex(insertionPoint);
  2702.  
  2703.         scrollToVisible = true;
  2704.         if (event.isReturnKey()) {
  2705.             Range r = new Range(selectedRange());
  2706.             if( _owner != null )
  2707.                 _owner.textWillChange(this,r);
  2708.  
  2709.             updateRect = insertReturn();
  2710.  
  2711.             if( _owner != null ) {
  2712.                 r.length = 1;
  2713.                 _owner.textDidChange(this,r);
  2714.             }
  2715.             draw(updateRect);
  2716.             TextView.returnRect(updateRect);
  2717.         } else if (event.isLeftArrowKey()) {
  2718.             if( event.isShiftKeyDown()) {
  2719.               newPos = _selection.orderedSelectionEnd() - 1;
  2720.               if( newPos < 0 )
  2721.                   newPos = 0;
  2722.               _selection.setRange(_selection.orderedSelectionStart(),
  2723.                                   newPos ,true);
  2724.             } else {
  2725.               if (insertionPoint == -1)
  2726.                   _selection.setRange(_selection.selectionStart(),
  2727.                                       _selection.selectionStart(),true);
  2728.               else {
  2729.                   newPos = insertionPoint - 1;
  2730.                   if( newPos < 0 )
  2731.                       newPos = 0;
  2732.                   _selection.setRange(newPos,newPos,true);
  2733.               }
  2734.             }
  2735.             _selectionChanged();
  2736.         } else if (event.isRightArrowKey()) {
  2737.             if( event.isShiftKeyDown()) {
  2738.                 _selection.setRange(_selection.orderedSelectionStart(),
  2739.                                     _selection.orderedSelectionEnd() + 1,false);
  2740.             } else {
  2741.                 if (insertionPoint == -1) {
  2742.                     _selection.setRange(_selection.selectionEnd(),
  2743.                                         _selection.selectionEnd());
  2744.                 } else {
  2745.                     _selection.setRange(insertionPoint + 1,
  2746.                                         insertionPoint + 1,false);
  2747.                 }
  2748.             }
  2749.             _selectionChanged();
  2750.         } else if (event.isUpArrowKey()) {
  2751.             currentPosition = _selection.orderedSelectionEndInfo();
  2752.  
  2753.             newPosition = positionForPoint(currentPosition._x,
  2754.                                            currentPosition._y - 1,false);
  2755.             if (newPosition != null) {
  2756.                 newPosition.representCharacterBeforeEndOfLine();
  2757.                 if( event.isShiftKeyDown()) {
  2758.                     if( newPosition._absPosition == currentPosition._absPosition &&
  2759.                         newPosition._absPosition > 0 )
  2760.                         _selection.setRange(_selection.orderedSelectionStart(),
  2761.                                             newPosition._absPosition-1,null,false,true);
  2762.                     else
  2763.                         _selection.setRange(_selection.orderedSelectionStart(),
  2764.                                             newPosition._absPosition,newPosition,false,true);
  2765.                 } else {
  2766.                     if(!(currentPosition._lineNumber == 0 &&
  2767.                          currentPosition._textRun._paragraph == _paragraphVector.elementAt(0))) {
  2768.                         _selection.setInsertionPoint(newPosition);
  2769.                     }
  2770.                 }
  2771.                 _selectionChanged();
  2772.             }
  2773.         } else if (event.isDownArrowKey()) {
  2774.             currentPosition = _selection.orderedSelectionEndInfo();
  2775.             currentPosition.representCharacterBeforeEndOfLine();
  2776.             newPosition = positionForPoint(currentPosition._x,
  2777.                                            currentPosition._y +
  2778.                                            currentPosition._lineHeight + 1,false);
  2779.  
  2780.             if (newPosition != null) {
  2781.                 if( event.isShiftKeyDown())
  2782.                     _selection.setRange(_selection.orderedSelectionStart(),
  2783.                                         newPosition._absPosition,newPosition,false,false);
  2784.                 else {
  2785.                     if( newPosition._textRun._paragraph != currentPosition._textRun._paragraph ||
  2786.                         newPosition._y != currentPosition._y)
  2787.                         _selection.setInsertionPoint(newPosition);
  2788.                 }
  2789.                 _selectionChanged();
  2790.             }
  2791.         } else if( event.isHomeKey()) {
  2792.             Range r = selectedRange();
  2793.             TextPositionInfo lineInfo = _selection.orderedSelectionEndInfo();
  2794.             Range selectedLine = lineForPosition(lineInfo);
  2795.             TextPositionInfo homeInfo;
  2796.  
  2797.             homeInfo = positionInfoForIndex(selectedLine.index);
  2798.             if( homeInfo != null ) {
  2799.                 if( homeInfo._y != lineInfo._y )
  2800.                     homeInfo.representCharacterAfterEndOfLine();
  2801.  
  2802.                 if( event.isShiftKeyDown()) {
  2803.                     if( homeInfo._absPosition != lineInfo._absPosition )
  2804.                         _selection.setRange(_selection.orderedSelectionStart(),
  2805.                                             homeInfo._absPosition,homeInfo,
  2806.                                             false,false);
  2807.                 } else
  2808.                     _selection.setInsertionPoint(homeInfo);
  2809.             }
  2810.             _selectionChanged();
  2811.         } else if( event.isEndKey())  {
  2812.             Range r = selectedRange();
  2813.             TextPositionInfo lineInfo = _selection.orderedSelectionEndInfo();
  2814.             Range selectedLine = lineForPosition(lineInfo);
  2815.             TextPositionInfo endInfo;
  2816.  
  2817.             endInfo = positionInfoForIndex(selectedLine.index + selectedLine.length);
  2818.             if( endInfo != null ) {
  2819.                 if( endInfo._y != lineInfo._y )
  2820.                     endInfo.representCharacterAfterEndOfLine();
  2821.  
  2822.                 if( event.isShiftKeyDown()) {
  2823.                     if(endInfo._absPosition  != lineInfo._absPosition)
  2824.                         _selection.setRange(_selection.orderedSelectionStart(),
  2825.                                             endInfo._absPosition,endInfo,
  2826.                                             false,false);
  2827.                 } else
  2828.                     _selection.setInsertionPoint(endInfo);
  2829.             }
  2830.             _selectionChanged();
  2831.         } else if (event.isBackspaceKey()) {
  2832.             Range r = new Range(selectedRange());
  2833.  
  2834.             if (_selection.isARange()) {
  2835.  
  2836.                  if( _owner != null )
  2837.                      _owner.textWillChange(this,r);
  2838.  
  2839.                 deleteSelection();
  2840.  
  2841.                 if( _owner != null ) {
  2842.                     r.length = 0;
  2843.                     _owner.textDidChange(this,r);
  2844.                 }
  2845.             } else {
  2846.                 if( _owner != null ) {
  2847.                     r.index--;
  2848.                     r.length = 1;
  2849.                     _owner.textWillChange(this,r);
  2850.                 }
  2851.  
  2852.                 fastDeleteChar(false);
  2853.  
  2854.                 if( _owner != null ) {
  2855.                     r.length = 0;
  2856.                     _owner.textDidChange(this,r);
  2857.                 }
  2858.             }
  2859.         } else if (event.isDeleteKey()) {
  2860.             Range r = selectedRange();
  2861.  
  2862.             if(_selection.isARange()) {
  2863.                 if( _owner != null )
  2864.                     _owner.textWillChange(this,r);
  2865.  
  2866.                 deleteSelection();
  2867.  
  2868.                 if( _owner != null ) {
  2869.                     r.length = 0;
  2870.                     _owner.textDidChange(this,r);
  2871.                 }
  2872.             } else {
  2873.               if(r.index < length()) {
  2874.                   r.length = 1;
  2875.                   if( _owner != null )
  2876.                       _owner.textWillChange(this,r);
  2877.  
  2878.                   fastDeleteChar(true);
  2879.  
  2880.                   r.length = 0;
  2881.                   if( _owner != null )
  2882.                       _owner.textDidChange(this,r);
  2883.               }
  2884.             }
  2885.         } else if (event.isPrintableKey()) {
  2886.             Range r = selectedRange();
  2887.             Range newSelection = allocateRange();
  2888.             if(event.isExtendedKeyEvent())
  2889.               replaceRangeWithString(r, ""+event.keyChar());
  2890.             else
  2891.             replaceRangeWithString( r , "" + (char)event.key );
  2892.             if(_typingAttributes.count() > 0) {
  2893.                 newSelection.index  = r.index;
  2894.                 newSelection.length = 1;
  2895.                 addAttributesForRangeWithoutNotification(_typingAttributes,newSelection);
  2896.                 clearTypingAttributes();
  2897.             }
  2898.             newSelection.index  = r.index + 1;
  2899.             newSelection.length = 0;
  2900.             selectRange(newSelection);
  2901.             recycleRange(newSelection);
  2902.         } else if( event.isTabKey()) {
  2903.             Range r = selectedRange();
  2904.             Range newSelection = allocateRange();
  2905.             replaceRangeWithString( r, "\t");
  2906.             newSelection.index = r.index + 1;
  2907.             newSelection.length=0;
  2908.             selectRange( newSelection );
  2909.             recycleRange( newSelection );
  2910.         } else {
  2911.             scrollToVisible = false;
  2912.         }
  2913.  
  2914.         if (scrollToVisible) {
  2915.             Range r = new Range(_selection.orderedSelectionEnd(),0);
  2916.             /* We do this for an edge case:
  2917.              * TextView contains an attachment that has the same size than the visible
  2918.              * rect and the user types before the textview */
  2919.             if( r.index > 0 ){
  2920.                 r.index--;
  2921.                 r.length++;
  2922.             }
  2923.             scrollRangeToVisible(r);
  2924.         }
  2925.  
  2926.         _selection.enableInsertionPoint();
  2927.     }
  2928.  
  2929.     private Range paragraphsRangeForRange(Range r) {
  2930.         TextParagraph   nextParagraph;
  2931.         int             count, i,lastLocation;
  2932.         Range result = allocateRange();
  2933.         boolean hasIndex=false;
  2934.         count = _paragraphVector.count();
  2935.  
  2936.         lastLocation = r.index + r.length;
  2937.         for (i = 0; i < count; i++) {
  2938.             nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  2939.  
  2940.             if( !hasIndex ) {
  2941.                 if (r.index >= nextParagraph._startChar &&
  2942.                     r.index < (nextParagraph._startChar + nextParagraph._charCount)) {
  2943.                     result.index = i;
  2944.                     hasIndex=true;
  2945.                 }
  2946.             }
  2947.             if( hasIndex ) {
  2948.                 if( lastLocation >= nextParagraph._startChar &&
  2949.                     lastLocation < (nextParagraph._startChar + nextParagraph._charCount)) {
  2950.                     result.length = i - result.index + 1;
  2951.                     break;
  2952.                 }
  2953.             }
  2954.         }
  2955.         return result;
  2956.     }
  2957.  
  2958.  
  2959.  
  2960.  
  2961.     /**
  2962.          * Returns the TextStyleRuns that comprise the current selection.  Forms new
  2963.          * TextStyleRuns if a selection endpoint falls within a single run.  If
  2964.          * an empty selection, returns (creates if necessary) a TextStyleRun at the
  2965.          * insertion point containing no characters.
  2966.          */
  2967.     private Vector createAndReturnRunsForRange(Range aRange) {
  2968.         TextParagraph           startParagraph, endParagraph, nextParagraph;
  2969.         TextStyleRun            startRun, endRun;
  2970.         Vector               runVector, tmpVector;
  2971.         int                     start, end, i;
  2972.         boolean                 sameParagraph;
  2973.         int absPosition,endAbsPosition;
  2974.  
  2975.         absPosition = aRange.index;
  2976.         endAbsPosition = aRange.index+aRange.length;
  2977.         startParagraph = _paragraphForIndex(absPosition);
  2978.         endParagraph   = _paragraphForIndex(endAbsPosition);
  2979.         sameParagraph = (startParagraph == endParagraph);
  2980.  
  2981.         if (aRange.length == 0 ) {
  2982.             /* if an insertion point, get an empty run */
  2983.             startRun = startParagraph.createNewRunAt(absPosition);
  2984.             if (startRun.charCount() > 0) {
  2985.                 startRun = startParagraph.createNewRunAt( absPosition );
  2986.             }
  2987.             runVector = TextView.newVector();
  2988.             runVector.addElement(startRun);
  2989.             return runVector;
  2990.         }
  2991.  
  2992.         /* cleave the last part of the first selected paragraph */
  2993.         startRun = startParagraph.runForCharPosition(absPosition);
  2994.         if( startRun.rangeIndex() != absPosition )
  2995.             startRun = startParagraph.createNewRunAt(absPosition);
  2996.  
  2997.  
  2998.         /* cleave the first part of the last selected paragraph */
  2999.         endRun = endParagraph.runForCharPosition(endAbsPosition);
  3000.         if ( endAbsPosition <= (endRun.rangeIndex() + endRun.charCount()-1)) {
  3001.             endRun = endParagraph.createNewRunAt(endAbsPosition);
  3002.             endRun = endParagraph.runBefore(endRun);
  3003.         }
  3004.  
  3005.         if (sameParagraph) {
  3006.             Vector result;
  3007.             if( startRun == endRun ) {
  3008.                 result = new Vector();
  3009.                 result.addElement(startRun);
  3010.                 return result;
  3011.             } else {
  3012.                 result = startParagraph.runsFromTo(startRun, endRun);
  3013.                 return result;
  3014.             }
  3015.         }
  3016.  
  3017.         runVector = TextView.newVector();
  3018.  
  3019.         runVector.addElement(startRun);
  3020.         tmpVector = startParagraph.runsAfter(startRun);
  3021.         runVector.addElementsIfAbsent(tmpVector);
  3022.         TextView.returnVector(tmpVector);
  3023.  
  3024.         /* grab the paragraphs in between */
  3025.         start = i = _paragraphVector.indexOfIdentical(startParagraph) + 1;
  3026.         end = _paragraphVector.indexOfIdentical(endParagraph);
  3027.         for (; i < end; i++) {
  3028.             nextParagraph = (TextParagraph)_paragraphVector.elementAt(i);
  3029.             runVector.addElementsIfAbsent(nextParagraph.runVector());
  3030.         }
  3031.  
  3032.         /* add the runs in the final paragraph */
  3033.         tmpVector = endParagraph.runsBefore(endRun);
  3034.         tmpVector.addElement( endRun );
  3035.         runVector.addElementsIfAbsent(tmpVector);
  3036.  
  3037.         /* This should not happen when setting
  3038.            some attribute accross several para */
  3039.         TextView.returnVector(tmpVector);
  3040.  
  3041.         return runVector;
  3042.     }
  3043.  
  3044.  
  3045.     /**
  3046.          * Changes the font of the currently-selected text.
  3047.          */
  3048.     private void processSetFont(Font aFont) {
  3049.         Range s = selectedRange();
  3050.         if( s.length > 0 ) {
  3051.             Hashtable newAttr = new Hashtable();
  3052.             newAttr.put( FONT_KEY , aFont );
  3053.             addAttributesForRange( newAttr , s);
  3054.         } else
  3055.             addTypingAttribute(FONT_KEY,aFont);
  3056.     }
  3057.  
  3058.  
  3059.  
  3060.     /**
  3061.          * Removes the current selection from the TextView and places the paragraphs
  3062.          * containing the text in the Vector <b>paragraphVector</b>.  If
  3063.          * <b>paragraphVector</b> is <b>null</b>, this method just throws away
  3064.          * the paragraphs.  This method resizes and redisplays the TextView.
  3065.          *
  3066.          * ALERT: This one the only remaining method not calling replaceRangeWithString()
  3067.          */
  3068.     private void deleteSelection(Vector paragraphVector) {
  3069.         Range r = selectedRange();
  3070.         deleteRange(r,paragraphVector);
  3071.         _selection.setRange(r.index,r.index);
  3072.         _selectionChanged();
  3073.     }
  3074.  
  3075.     private void deleteRange(Range aRange, Vector paragraphVector) {
  3076.         TextParagraph           newParagraph = null, nextParagraph,
  3077.             startParagraph, endParagraph;
  3078.         TextPositionInfo        rangeStartInfo, rangeEndInfo;
  3079.         TextStyleRun            startRun, endRun;
  3080.         Vector                  runVector;
  3081.         int                     i, start, end, currentHeight,index;
  3082.         boolean                 sameParagraph;
  3083.         Range dirtyRange;
  3084.  
  3085.         if( aRange.length == 0 )
  3086.             return;
  3087.  
  3088.         dirtyRange = allocateRange(aRange.index , length() - aRange.index);
  3089.         dirtyRange( dirtyRange );
  3090.         recycleRange( dirtyRange );
  3091.  
  3092.         notifyAttachmentsForRange(aRange,false);
  3093.  
  3094.         rangeStartInfo = positionInfoForIndex( aRange.index );
  3095.         rangeEndInfo   = positionInfoForIndex( aRange.index + aRange.length);
  3096.         startParagraph = rangeStartInfo._textRun._paragraph;
  3097.         endParagraph = rangeEndInfo._textRun._paragraph;
  3098.         sameParagraph = (startParagraph == endParagraph);
  3099.  
  3100.         /* grab the last part of the first selected paragraph */
  3101.         startRun = rangeStartInfo._textRun;
  3102.         if( startRun.rangeIndex() != rangeStartInfo._absPosition )
  3103.             startRun = startParagraph.createNewRunAt(rangeStartInfo._absPosition);
  3104.         if (!sameParagraph) {
  3105.             runVector = startParagraph.runsAfter(startRun);
  3106.             startParagraph.removeRun(startRun);
  3107.             startParagraph.removeRuns(runVector);
  3108.  
  3109.             if (paragraphVector != null) {
  3110.                 newParagraph = new TextParagraph(this);
  3111.                 newParagraph.setFormat(startParagraph._format);
  3112.                 newParagraph.addRun(startRun);
  3113.                 newParagraph.addRuns(runVector);
  3114.                 paragraphVector.addElement(newParagraph);
  3115.             }
  3116.  
  3117.             runVector.removeAllElements();
  3118.             TextView.returnVector(runVector);
  3119.         }
  3120.  
  3121.         /* grab the first part of the last selected paragraph */
  3122.         endRun = rangeEndInfo._textRun;
  3123.         if(endRun.rangeIndex() != rangeEndInfo._absPosition)
  3124.             endRun = endParagraph.createNewRunAt(rangeEndInfo._absPosition);
  3125.         if (sameParagraph) {
  3126.             runVector = startParagraph.runsFromTo(startRun, endRun);
  3127.             runVector.removeElement(endRun);
  3128.             startParagraph.removeRuns(runVector);
  3129.  
  3130.             if (paragraphVector != null) {
  3131.                 newParagraph = new TextParagraph(this);
  3132.                 newParagraph.setFormat(startParagraph._format);
  3133.                 newParagraph.addRuns(runVector);
  3134.                 paragraphVector.addElement(newParagraph);
  3135.             }
  3136.  
  3137.             runVector.removeAllElements();
  3138.             TextView.returnVector(runVector);
  3139.         } else {
  3140.             /* grab the paragraphs in between */
  3141.             start = i = _paragraphVector.indexOfIdentical(startParagraph) + 1;
  3142.             end = _paragraphVector.indexOfIdentical(endParagraph);
  3143.             for (; i < end; i++) {
  3144.                 nextParagraph =
  3145.                     (TextParagraph)_paragraphVector.removeElementAt(start);
  3146.                 if (paragraphVector != null) {
  3147.                     paragraphVector.addElement(nextParagraph);
  3148.                 }
  3149.             }
  3150.  
  3151.             /* add the runs in the final paragraph */
  3152.             runVector = endParagraph.runsBefore(endRun);
  3153.             endParagraph.removeRuns(runVector);
  3154.  
  3155.             if (paragraphVector != null) {
  3156.                 newParagraph = new TextParagraph(this);
  3157.                 newParagraph.setFormat(startParagraph._format);
  3158.                 newParagraph.addRuns(runVector);
  3159.                 paragraphVector.addElement(newParagraph);
  3160.             }
  3161.  
  3162.             runVector.removeAllElements();
  3163.             TextView.returnVector(runVector);
  3164.  
  3165.             /* combine last paragraph with first */
  3166.             startParagraph.subsumeParagraph(endParagraph);
  3167.             _paragraphVector.removeElement(endParagraph);
  3168.         }
  3169.         formatParagraph(startParagraph);
  3170.     }
  3171.  
  3172.     private void deleteSelection() {
  3173.         deleteSelection(null);
  3174.     }
  3175.  
  3176.  
  3177.     boolean isOnlyParagraph(TextParagraph aParagraph) {
  3178.         if (_paragraphVector.count() == 1 &&
  3179.             _paragraphVector.contains(aParagraph)) {
  3180.             return true;
  3181.         }
  3182.         return false;
  3183.     }
  3184.  
  3185.     int selectionStart() {
  3186.         return _selection.selectionStart();
  3187.     }
  3188.  
  3189.     TextPositionInfo selectionStartInfo() {
  3190.         return _selection.selectionStartInfo();
  3191.     }
  3192.  
  3193.     int selectionEnd() {
  3194.         return _selection.selectionEnd();
  3195.     }
  3196.  
  3197.     TextPositionInfo selectionEndInfo() {
  3198.         return _selection.selectionEndInfo();
  3199.     }
  3200.  
  3201.     boolean hasSelectionRange() {
  3202.         return _selection.isARange();
  3203.     }
  3204.  
  3205.     TextParagraph lastParagraph() {
  3206.         return (TextParagraph)_paragraphVector.lastElement();
  3207.     }
  3208.  
  3209.     char characterAt(int absPosition) {
  3210.         TextParagraph   theParagraph;
  3211.  
  3212.         if (absPosition < 0 || absPosition > _charCount) {
  3213.             return '\0';
  3214.         }
  3215.  
  3216.         theParagraph = _paragraphForIndex(absPosition);
  3217.         if (theParagraph == null) {
  3218.             theParagraph = lastParagraph();
  3219.         }
  3220.  
  3221.         return theParagraph.characterAt(absPosition);
  3222.     }
  3223.  
  3224.     int _positionOfPreviousWord(int absPosition) {
  3225.         char            previousChar;
  3226.         boolean         done = false;
  3227.  
  3228.         if (absPosition == 0) {
  3229.             return 0;
  3230.         }
  3231.  
  3232.         previousChar = characterAt(absPosition--);
  3233.         if (previousChar == '\n') {
  3234.             return absPosition + 1;
  3235.         }
  3236.  
  3237.         if (previousChar == ' ' || previousChar == '\t') {
  3238.             do {
  3239.                 previousChar = characterAt(absPosition--);
  3240.                 done = (previousChar != ' ' && previousChar != '\t') ||
  3241.                     (previousChar == '\n');
  3242.             } while (absPosition > -1 && !done);
  3243.         } else {
  3244.             do {
  3245.                 previousChar = characterAt(absPosition--);
  3246.                 done = (previousChar == ' ' || previousChar == '\t' ||
  3247.                         (previousChar >= '!' && previousChar <= '/') ||
  3248.                         (previousChar >= ':' && previousChar <= '@') ||
  3249.                         (previousChar >= '[' && previousChar <= '\'') ||
  3250.                         (previousChar >= '{' && previousChar <= '~') ||
  3251.                         previousChar == '\n');
  3252.             } while (absPosition > -1 && !done);
  3253.         }
  3254.  
  3255.         if (done) {
  3256.             return absPosition + 2;
  3257.         }
  3258.  
  3259.         return 0;
  3260.     }
  3261.  
  3262.     int _positionOfNextWord(int absPosition) {
  3263.         char            previousChar, nextChar;
  3264.         boolean         done = false;
  3265.  
  3266.         if (absPosition >= _charCount) {
  3267.             return _charCount;
  3268.         }
  3269.  
  3270.         if (absPosition > 0) {
  3271.             previousChar = characterAt(absPosition - 1);
  3272.             if (previousChar == '\n') {
  3273.                 return absPosition - 1;
  3274.             }
  3275.         }
  3276.  
  3277.         nextChar = characterAt(absPosition++);
  3278.         if (nextChar == ' ' || nextChar == '\t') {
  3279.             do {
  3280.                 previousChar = nextChar;
  3281.                 nextChar = characterAt(absPosition++);
  3282.                 done = (nextChar != ' ' && nextChar != '\t') ||
  3283.                     (nextChar == '\n');
  3284.             } while (absPosition < _charCount && !done);
  3285.         } else {
  3286.             do {
  3287.                 previousChar = nextChar;
  3288.                 nextChar = characterAt(absPosition++);
  3289.                 done = (nextChar == ' ' || nextChar == '\t' ||
  3290.                         (nextChar >= '!' && nextChar <= '/') ||
  3291.                         (nextChar >= ':' && nextChar <= '@') ||
  3292.                         (nextChar >= '[' && nextChar <= '\'') ||
  3293.                         (nextChar >= '{' && nextChar <= '~') ||
  3294.                         nextChar == '\n');
  3295.             } while (absPosition < _charCount && !done);
  3296.         }
  3297.  
  3298.         if (done) {
  3299.             return absPosition - 1;
  3300.         }
  3301.  
  3302.         return _charCount;
  3303.     }
  3304.  
  3305.     private void hideInsertionPoint() {
  3306.         if( insertionPointVisible ) {
  3307.             insertionPointVisible=false;
  3308.         }
  3309.     }
  3310.  
  3311.     private void showInsertionPoint() {
  3312.         if(! insertionPointVisible ) {
  3313.             insertionPointVisible = true;
  3314.         }
  3315.     }
  3316.  
  3317.  
  3318.  
  3319.  
  3320.     /* rect cache */
  3321.  
  3322.     static Rect newRect(int x, int y, int width, int height) {
  3323.         Rect    theRect;
  3324.  
  3325.         synchronized(_rectCache) {
  3326.             if (!_shouldCache || _rectCache.isEmpty()) {
  3327.                 return new Rect(x, y, width, height);
  3328.             }
  3329.  
  3330.             theRect = (Rect)_rectCache.removeLastElement();
  3331.         }
  3332.         theRect.setBounds(x, y, width, height);
  3333.  
  3334.         return theRect;
  3335.     }
  3336.  
  3337.     /**
  3338.          * Returns a Rect from the Rect cache whose origin and size match
  3339.          * templateRect.  Creates a new Rect if the cache is empty.
  3340.          */
  3341.     static Rect newRect(Rect templateRect) {
  3342.         Rect    theRect;
  3343.  
  3344.         synchronized(_rectCache) {
  3345.             if (!_shouldCache || _rectCache.isEmpty()) {
  3346.                 return new Rect(templateRect);
  3347.             }
  3348.  
  3349.             theRect = (Rect)_rectCache.removeLastElement();
  3350.         }
  3351.         theRect.setBounds(templateRect);
  3352.  
  3353.         return theRect;
  3354.     }
  3355.  
  3356.     /**
  3357.          * Returns a Rect from the Rect cache with origin (0, 0) and zero size.
  3358.          * Equivalent to
  3359.          * <pre>
  3360.          *  r = TextView.newRect(0, 0, 0, 0);
  3361.          * </pre>
  3362.          * Creates a new Rect if the cache is empty.
  3363.          */
  3364.     static Rect newRect() {
  3365.         return TextView.newRect(0, 0, 0, 0);
  3366.     }
  3367.  
  3368.  
  3369.     /**
  3370.          * Places r back in the Rect cache (if the cache isn't full).
  3371.          */
  3372.     static void returnRect(Rect r) {
  3373.         if( r == null )
  3374.             return;
  3375.         if (!_shouldCache) {
  3376.             return;
  3377.         }
  3378.  
  3379.         synchronized(_rectCache) {
  3380.             if (_rectCache.count() < 50) {
  3381.                 _rectCache.addElement(r);
  3382.             }
  3383.         }
  3384.     }
  3385.  
  3386.     /**
  3387.          * Places the Rects contained in rectVector back in the Rect cache
  3388.          * (if the cache isn't full) and empties the Vector.
  3389.          */
  3390.     static void returnRects(Vector rectVector) {
  3391.         int     i;
  3392.  
  3393.         if (rectVector == null || !_shouldCache) {
  3394.             return;
  3395.         }
  3396.  
  3397.         i = rectVector.count();
  3398.         while (i-- > 0) {
  3399.             TextView.returnRect((Rect)rectVector.elementAt(i));
  3400.         }
  3401.  
  3402.         rectVector.removeAllElements();
  3403.     }
  3404.  
  3405.     /**
  3406.          * Enables and disables Rect caching.  With setShouldCacheRects(false),
  3407.          * TextView.newRect() methods create new Rects and TextView.returnRect()
  3408.          * methods do
  3409.          * nothing with the Rects they're given.  Disabling Rect caching can help
  3410.          * you track down problems in your code of returning Rects to the cache
  3411.          * while accidentally continuing to maintain a reference to them.
  3412.          */
  3413.     static void setShouldCacheRects(boolean flag) {
  3414.         synchronized(_rectCache) {
  3415.             _shouldCache = flag;
  3416.             if (!_shouldCache) {
  3417.                 _rectCache.removeAllElements();
  3418.             }
  3419.         }
  3420.     }
  3421.  
  3422.     static Vector newVector() {
  3423.         Vector       theVector;
  3424.  
  3425.         synchronized(_vectorCache) {
  3426.             if (!_shouldCache || _vectorCache.isEmpty()) {
  3427.                 return new Vector();
  3428.             }
  3429.  
  3430.             theVector = (Vector)_vectorCache.removeLastElement();
  3431.         }
  3432.  
  3433.         return theVector;
  3434.     }
  3435.  
  3436.     static void returnVector(Vector aVector) {
  3437.         if (!_shouldCache) {
  3438.             return;
  3439.         }
  3440.  
  3441.         synchronized(_vectorCache) {
  3442.             if (aVector != null && _vectorCache.count() < 15) {
  3443.                 aVector.removeAllElements();
  3444.                 _vectorCache.addElement(aVector);
  3445.             }
  3446.         }
  3447.     }
  3448.  
  3449.     static void setShouldCacheVectors(boolean flag) {
  3450.         synchronized(_vectorCache) {
  3451.             _shouldCache = flag;
  3452.  
  3453.             if (!_cacheVectors) {
  3454.                 _vectorCache.removeAllElements();
  3455.             }
  3456.         }
  3457.     }
  3458.  
  3459.     private void _selectionChanged() {
  3460.         /* Make sure we do not call selectionChanged
  3461.          * or reset typing attributes when the selection
  3462.          * did not change
  3463.          */
  3464.         int start = _selection.selectionStart();
  3465.         int  end  = _selection.selectionEnd();
  3466.         if( start == _wasSelectedRange.index &&
  3467.             (end - start) == _wasSelectedRange.length )
  3468.             return;
  3469.  
  3470.         _wasSelectedRange.index = start;
  3471.         _wasSelectedRange.length = end - start;
  3472.         clearTypingAttributes();
  3473.         if( _owner != null ) {
  3474.             _owner.selectionDidChange(this);
  3475.         }
  3476.     }
  3477.  
  3478.     void dirtyRange(Range aRange) {
  3479.         /* If we are dirty don't spend some time finding the dirty rect */
  3480.  
  3481.         if( isDirty() && dirtyRect == null )
  3482.             return;
  3483.         else {
  3484.             Range r = allocateRange(aRange);
  3485.             Rect visibleRect;
  3486.             visibleRect = new Rect();
  3487.             computeVisibleRect( visibleRect );
  3488.             r.intersectWith(0,length());
  3489.             if( _superview != null && r != null && !r.isNullRange() && r.length > 0 ) {
  3490.                 Rect rect;
  3491.                 Vector dirtyRects = rectsForRange(r,visibleRect);
  3492.                 int i,c;
  3493.                 for(i=0,c=dirtyRects.count() ; i < c ; i++ ) {
  3494.                     rect = (Rect) dirtyRects.elementAt(i);
  3495.                     rect.x = 0;
  3496.                     rect.width = bounds.width;
  3497.                     if( rect.width > 0 && rect.height > 0 ) {
  3498.                         this.addDirtyRect((Rect)rect);
  3499.                     }
  3500.                 }
  3501.             }
  3502.             recycleRange( r );
  3503.         }
  3504.     }
  3505.  
  3506.  
  3507.     private TextParagraphFormat _formatForTextPositionInfo(TextPositionInfo info) {
  3508.         TextParagraphFormat f = info._textRun.paragraph().format();
  3509.         if( f == null )
  3510.             f = (TextParagraphFormat) _defaultAttributes.get(PARAGRAPH_FORMAT_KEY);
  3511.         return f;
  3512.     }
  3513.  
  3514.     private TextPositionInfo positionInfoForNextLine(TextPositionInfo info ) {
  3515.         TextPositionInfo result;
  3516.  
  3517.         int nextLineFirstChar = info._textRun.paragraph().characterStartingLine( info._lineNumber + 1);
  3518.         if( nextLineFirstChar == -1 ) {
  3519.  
  3520.             int nextParagraphIndex = _paragraphVector.indexOfIdentical((Object)info._textRun.paragraph() ) + 1;
  3521.             TextParagraph nextParagraph;
  3522.  
  3523.             if( nextParagraphIndex < _paragraphVector.count()) {
  3524.                 nextParagraph = (TextParagraph) _paragraphVector.elementAt(nextParagraphIndex);
  3525.                 return positionInfoForIndex( nextParagraph._startChar );
  3526.             } else
  3527.                 return null;
  3528.         }
  3529.  
  3530.         result = positionInfoForIndex( nextLineFirstChar );
  3531.         return result;
  3532.     }
  3533.  
  3534.  
  3535.  
  3536.  
  3537.  
  3538.  
  3539.     private TextStyleRun runBefore(TextStyleRun aRun) {
  3540.         TextStyleRun run = aRun.paragraph().runBefore(aRun);
  3541.         if( run == null ) {
  3542.             int index = _paragraphVector.indexOfIdentical( aRun.paragraph());
  3543.             if( index > 0 )
  3544.                 return ((TextParagraph)_paragraphVector.elementAt(index - 1)).lastRun();
  3545.         }
  3546.         return run;
  3547.     }
  3548.  
  3549.     private TextStyleRun runAfter(TextStyleRun aRun) {
  3550.         TextStyleRun run = aRun.paragraph().runAfter(aRun);
  3551.         if( run == null ) {
  3552.             int index = _paragraphVector.indexOfIdentical( aRun.paragraph());
  3553.             if( index < (_paragraphVector.count()-1))
  3554.                 return ((TextParagraph)_paragraphVector.elementAt(index + 1)).firstRun();
  3555.         }
  3556.         return run;
  3557.     }
  3558.  
  3559.     private Range linkRangeForPosition(int index) {
  3560.         TextStyleRun run,orig,firstRun,lastRun;
  3561.         Hashtable attr;
  3562.         String url;
  3563.         Range result;
  3564.  
  3565.         orig = _runForIndex(index);
  3566.         if( (attr = orig.attributes()) != null &&
  3567.             (url = (String) attr.get(LINK_KEY)) != null) {
  3568.  
  3569.             run = firstRun = orig;
  3570.             while( true ) {
  3571.                 run = runBefore(run);
  3572.                 if( run == null ||
  3573.                     (attr = run.attributes()) == null ||
  3574.                     (!url.equals((String)attr.get(LINK_KEY))))
  3575.                     break;
  3576.                 firstRun = run;
  3577.             }
  3578.  
  3579.  
  3580.             run = lastRun = orig;
  3581.             while( true ) {
  3582.                 run = runAfter( run );
  3583.                 if( run == null ||
  3584.                     (attr = run.attributes()) == null ||
  3585.                     (!url.equals((String)attr.get(LINK_KEY))))
  3586.                     break;
  3587.                 lastRun = run;
  3588.             }
  3589.  
  3590.             result = firstRun.range();
  3591.             result.unionWith(lastRun.range());
  3592.             return result;
  3593.         }
  3594.         return null;
  3595.     }
  3596.  
  3597.     private void highlightLinkWithRange(Range aRange,boolean flag) {
  3598.         Range r;
  3599.         int i,c;
  3600.  
  3601.         if( flag )
  3602.             addAttributeForRange( LINK_IS_PRESSED_KEY , "", aRange);
  3603.         else
  3604.             removeAttributeForRange( LINK_IS_PRESSED_KEY,aRange);
  3605.  
  3606.         /** Collect empty runs created during the change of attributes */
  3607.         r = paragraphsRangeForRange(aRange);
  3608.         for(i=r.index,c=r.index+r.length ; i < c ; i++)
  3609.             ((TextParagraph)_paragraphVector.elementAt(i)).collectEmptyRuns();
  3610.     }
  3611.  
  3612.     private boolean runUnderMouse(TextStyleRun run,int x,int y) {
  3613.         int i,c;
  3614.         Vector rects = rectsForRange(run.range());
  3615.         for(i=0,c=rects.count(); i < c ; i++ )
  3616.             if( ((Rect)rects.elementAt(i)).contains(x,y))
  3617.                 return true;
  3618.  
  3619.         return false;
  3620.     }
  3621.  
  3622.     private boolean runsUnderMouse(Vector runs,int x,int y) {
  3623.         int i,c;
  3624.         Range r;
  3625.         TextStyleRun run;
  3626.  
  3627.         for(i=0,c=runs.count() ; i < c ; i++ ) {
  3628.             r = (Range)runs.elementAt(i);
  3629.             run = _runForIndex(r.index);
  3630.             if(runUnderMouse(run,x,y))
  3631.                 return true;
  3632.         }
  3633.         return false;
  3634.     }
  3635.  
  3636.     boolean lastParagraphIsEmpty() {
  3637.         return (lastParagraph()._charCount == 0);
  3638.     }
  3639.  
  3640.  
  3641.     char charAt(int index) {
  3642.         String s = stringForRange( new Range( index , 1));
  3643.         if( s != null && s.length() > 0 )
  3644.             return s.charAt(0);
  3645.         else
  3646.             return '\0';
  3647.     }
  3648.  
  3649.     boolean isWordCharacter(char c) {
  3650.         if( c >= '0' && c <= '9' ||
  3651.             c >= 'A' && c <= 'Z' ||
  3652.             c >= 'a' && c <= 'z' )
  3653.             return true;
  3654.         else
  3655.             return false;
  3656.     }
  3657.  
  3658.     Range groupForIndex(int index) {
  3659.         int length = length();
  3660.         int relFirstIndex,relLastIndex;
  3661.         int i,l;
  3662.         char c;
  3663.  
  3664.         i = index;
  3665.         c = charAt(i);
  3666.  
  3667.         if( c == '\n') {
  3668.             return new Range(i,1);
  3669.         }
  3670.  
  3671.         if( c == ' ' || c == '\t' ) {
  3672.             while(i>0) {
  3673.                 c = charAt(i);
  3674.                 if( c == ' ' || c == '\t' )
  3675.                     i--;
  3676.                 else
  3677.                     break;
  3678.             }
  3679.             relFirstIndex = i+1;
  3680.  
  3681.             i = index;
  3682.  
  3683.             while(i<length()) {
  3684.                 c = charAt(i);
  3685.                 if( c == ' ' || c == '\t')
  3686.                     i++;
  3687.                 else
  3688.                     break;
  3689.             }
  3690.             relLastIndex = i-1;
  3691.             return new Range(relFirstIndex,relLastIndex - relFirstIndex +1);
  3692.         }
  3693.  
  3694.         if( !isWordCharacter(c) ) {
  3695.             return new Range( index, 1);
  3696.         }
  3697.  
  3698.         relFirstIndex = i;
  3699.         while( relFirstIndex > 0 ) {
  3700.             c = charAt(relFirstIndex-1);
  3701.             if(!isWordCharacter(c))
  3702.                 break;
  3703.             relFirstIndex--;
  3704.         }
  3705.  
  3706.  
  3707.         relLastIndex = i;
  3708.         while( relLastIndex < (length-1)) {
  3709.             c = charAt(relLastIndex+1);
  3710.             if(!isWordCharacter(c))
  3711.                 break;
  3712.             relLastIndex++;
  3713.         }
  3714.  
  3715.         return new Range(relFirstIndex, relLastIndex - relFirstIndex + 1);
  3716.     }
  3717.  
  3718.     Range lineForPosition(TextPositionInfo info) {
  3719.         TextParagraph p = info._textRun._paragraph;
  3720.         int lineBegin,lineEnd;
  3721.  
  3722.         if( p != null ) {
  3723.             if( info._lineNumber == 0 )
  3724.                 lineBegin = p._startChar;
  3725.             else
  3726.                 lineBegin = p._startChar + p._lineBreaks[ info._lineNumber - 1 ];
  3727.  
  3728.             lineEnd = p._startChar + p._lineBreaks[info._lineNumber] - 1;
  3729.             return new Range( lineBegin , lineEnd - lineBegin + 1);
  3730.         }
  3731.         return new Range();/** null range */
  3732.     }
  3733.  
  3734.     void replaceContentWithString(String aString) {
  3735.         TextParagraph p;
  3736.         TextStyleRun  r;
  3737.         int i,c,nextBreak;
  3738.         int firstIndex,lastIndex;
  3739.  
  3740.         notifyAttachmentsForRange(new Range(0,length()),false);
  3741.         _paragraphVector.removeAllElements();
  3742.  
  3743.         i = 0;
  3744.         c = aString.length();
  3745.         while( i < c ) {
  3746.             nextBreak = aString.indexOf('\n',i);
  3747.             if( nextBreak == -1 ) {
  3748.                 firstIndex = i;
  3749.                 lastIndex  = c;
  3750.                 i = c;
  3751.             } else {
  3752.                 firstIndex = i;
  3753.                 lastIndex  = nextBreak;
  3754.                 i = nextBreak + 1;
  3755.             }
  3756.             p = new TextParagraph(this);
  3757.             r = new TextStyleRun(p,aString,firstIndex,lastIndex,null);
  3758.             p.addRun( r );
  3759.             _paragraphVector.addElement(p);
  3760.         }
  3761.  
  3762.         /* Never leave a textview without any paragraph
  3763.          * or add the last \n
  3764.          */
  3765.         if( _paragraphVector.count() == 0 || aString.charAt(c-1) == '\n') {
  3766.             p = new TextParagraph(this);
  3767.             r = new TextStyleRun(p,"",null);
  3768.             p.addRun(r);
  3769.             _paragraphVector.addElement(p);
  3770.         }
  3771.         this.setDirty(true);
  3772.         reformatAll();
  3773.     }
  3774.  
  3775.    Vector rectsForRange(Range aRange,Rect maxRect) {
  3776.        Range r = allocateRange(aRange.index,aRange.length);
  3777.        Vector result = new Vector();
  3778.        TextPositionInfo start,end,info;
  3779.        TextParagraphFormat f;
  3780.        Rect rect,lastRect;
  3781.        int i,c,lastY;
  3782.        boolean done;
  3783.        TextParagraph p;
  3784.        int lineRemainder;
  3785.  
  3786.        r.intersectWith(0,length());
  3787.  
  3788.        if( r.length == 0 || r.isNullRange()) {
  3789.            recycleRange(r);
  3790.            return result;
  3791.        }
  3792.  
  3793.        start = positionInfoForIndex( r.index );
  3794.        if( start._endOfLine && !start._endOfParagraph )
  3795.            start.representCharacterAfterEndOfLine();
  3796.        end   = positionInfoForIndex( r.index + r.length());
  3797.        //System.out.println("Start is " + start);
  3798.        //System.out.println("End is " + end);
  3799.        if( start == null || end == null ){
  3800.            recycleRange(r);
  3801.            return result;
  3802.        }
  3803.  
  3804.        if( start._textRun.paragraph() == end._textRun.paragraph() &&
  3805.            start._lineNumber == end._lineNumber ) {
  3806.  
  3807.            f = _formatForTextPositionInfo(start);
  3808.            if( end._endOfLine ) {
  3809.                p = start._textRun.paragraph();
  3810.                lineRemainder = p._lineRemainders[start._lineNumber];
  3811.                switch( f._justification ) {
  3812.                case Graphics.CENTERED:
  3813.                    result.addElement(new Rect(start._x,start._y,
  3814.                                               bounds.width - f._rightMargin - start._x -
  3815.                                               (lineRemainder / 2),
  3816.                                               start._lineHeight));
  3817.                    break;
  3818.                case Graphics.RIGHT_JUSTIFIED:
  3819.                    result.addElement(new Rect(start._x,start._y,
  3820.                                               bounds.width - f._rightMargin - start._x,
  3821.                                               start._lineHeight));
  3822.                    break;
  3823.                case Graphics.LEFT_JUSTIFIED:
  3824.                default:
  3825.                    result.addElement(new Rect(start._x,start._y,
  3826.                                               bounds.width - f._rightMargin - start._x -
  3827.                                               lineRemainder,
  3828.                                               start._lineHeight));
  3829.                    break;
  3830.  
  3831.                }
  3832.            } else
  3833.                result.addElement(new Rect(start._x,start._y,end._x - start._x , start._lineHeight));
  3834.            recycleRange(r);
  3835.            return result;
  3836.        }
  3837.  
  3838.        f = _formatForTextPositionInfo(start);
  3839.        p = start._textRun.paragraph();
  3840.        lineRemainder = p._lineRemainders[start._lineNumber];
  3841.        switch( f._justification ) {
  3842.        case Graphics.CENTERED:
  3843.            rect = new Rect(start._x ,start._y, bounds.width - f._rightMargin - start._x -
  3844.                            (lineRemainder/2), start._lineHeight);
  3845.            break;
  3846.        case Graphics.RIGHT_JUSTIFIED:
  3847.            rect = new Rect(start._x ,start._y, bounds.width - f._rightMargin - start._x ,
  3848.                            start._lineHeight);
  3849.            break;
  3850.        case Graphics.LEFT_JUSTIFIED:
  3851.        default:
  3852.            rect = new Rect(start._x ,start._y, bounds.width - f._rightMargin - start._x -
  3853.                            lineRemainder, start._lineHeight);
  3854.            break;
  3855.        }
  3856.        /** If width == 0 add the rect anyway to refresh empty lines
  3857.         *  while selecting
  3858.         */
  3859.        if( rect.height > 0 )
  3860.            result.addElement(rect);
  3861.  
  3862.        rect = new Rect(0,0,0,0);
  3863.        info = start;
  3864.  
  3865.        done = false;
  3866.        lastY = -1;
  3867.        while (!done)  {
  3868.            info = positionInfoForNextLine( info );
  3869.            if( info == null )
  3870.                break;
  3871.  
  3872.            if( info._endOfLine && !info._endOfParagraph)
  3873.                info.representCharacterAfterEndOfLine();
  3874.  
  3875.            if(info._y <= lastY) { /** No more progress. Layout is not done yet
  3876.                                    *  new text will be layout later.
  3877.                                    */
  3878.                break;
  3879.            } else
  3880.                lastY = info._y;
  3881.  
  3882.            if( maxRect != null ) {
  3883.                if( info._y  <  maxRect.y )
  3884.                    continue;
  3885.  
  3886.                if( info._y > (maxRect.y + maxRect.height)) {
  3887.                    done = true;
  3888.                    break;
  3889.                }
  3890.            }
  3891.  
  3892.  
  3893.            f = _formatForTextPositionInfo( info );
  3894.            if( info._textRun.paragraph() != end._textRun.paragraph() ||
  3895.                info._lineNumber < end._lineNumber ) {
  3896.                rect.x = info._x;
  3897.                rect.y = info._y;
  3898.                p = info._textRun.paragraph();
  3899.                lineRemainder = p._lineRemainders[info._lineNumber];
  3900.                switch( f._justification) {
  3901.                case Graphics.CENTERED:
  3902.                    rect.width  = bounds.width - f._rightMargin - rect.x -
  3903.                        (lineRemainder / 2);
  3904.                    break;
  3905.                case Graphics.RIGHT_JUSTIFIED:
  3906.                    rect.width  = bounds.width - f._rightMargin - rect.x;
  3907.                    break;
  3908.                case Graphics.LEFT_JUSTIFIED:
  3909.                default:
  3910.                    rect.width  = bounds.width - f._rightMargin - rect.x -
  3911.                        lineRemainder;
  3912.                    break;
  3913.                }
  3914.                rect.height = info._lineHeight;
  3915.            } else {
  3916.                rect.x = info._x;
  3917.                rect.y = info._y;
  3918.                rect.width  = end._x - rect.x;
  3919.                rect.height = info._lineHeight;
  3920.                done = true;
  3921.            }
  3922.  
  3923.            if( rect.height > 0 ) {
  3924.                lastRect = (Rect) result.lastElement();
  3925.                if( lastRect != null && lastRect.x == rect.x && lastRect.width == rect.width ) {
  3926.                    lastRect.height = (rect.y + rect.height) - lastRect.y;
  3927.                } else {
  3928.                    result.addElement(new Rect(rect));
  3929.                }
  3930.            }
  3931.        }
  3932.        recycleRange(r);
  3933.        return result;
  3934.    }
  3935.  
  3936.     Vector rangesOfVisibleAttachmentsWithBitmap(Bitmap aBitmap) {
  3937.         Vector result = new Vector();
  3938.         TextPositionInfo info;
  3939.         TextStyleRun     run;
  3940.         int firstIndex,lastIndex;
  3941.         Rect r = new Rect();
  3942.         Hashtable attributes;
  3943.         TextAttachment attachment;
  3944.  
  3945.         computeVisibleRect(r);
  3946.  
  3947.  
  3948.         info = positionForPoint(r.x,r.y,true);
  3949.         if( info == null )
  3950.             firstIndex = 0;
  3951.         else if( info._absPosition > 0 )
  3952.             firstIndex = info._absPosition - 1;
  3953.         else
  3954.             firstIndex = 0;
  3955.  
  3956.         info = positionForPoint(r.x + r.width , r.y + r.height,true);
  3957.         if( info == null )
  3958.             lastIndex = length() - 1;
  3959.         else if( info._absPosition < (length()-1))
  3960.             lastIndex = info._absPosition + 1;
  3961.         else
  3962.             lastIndex = length() - 1;
  3963.  
  3964.         run = _runForIndex(firstIndex);
  3965.         while( run != null ) {
  3966.             attributes = run.attributes();
  3967.             if(attributes != null && (attachment = (TextAttachment)
  3968.                                       attributes.get(TEXT_ATTACHMENT_KEY)) != null ) {
  3969.                 if( attachment instanceof ImageAttachment ) {
  3970.                     if( ((ImageAttachment)attachment).image() == (Image) aBitmap)
  3971.                         result.addElement(run.range());
  3972.                 }
  3973.             }
  3974.             run = runAfter( run );
  3975.             if( run.rangeIndex() > lastIndex )
  3976.                 break;
  3977.         }
  3978.  
  3979.         return result;
  3980.     }
  3981.  
  3982.     void refreshBitmap(Object data) {
  3983.         Vector ranges;
  3984.         Bitmap bm = (Bitmap) data;
  3985.         Range attachmentRange;
  3986.         Rect imageRect;
  3987.         Rect r;
  3988.         int i,c;
  3989.         int j,d;
  3990.  
  3991.         ranges = rangesOfVisibleAttachmentsWithBitmap(bm);
  3992.  
  3993.         for(j=0,d=ranges.count() ; j < d ; j++ ) {
  3994.           attachmentRange = (Range) ranges.elementAt(j);
  3995.           if( !attachmentRange.isNullRange()) {
  3996.               Vector v = rectsForRange(attachmentRange);
  3997.               imageRect = bm.updateRect();
  3998.  
  3999.               if (v.count() > 0 ) {
  4000.                   r = (Rect) v.elementAt(0);
  4001.                   for(i=1,c=v.count() ; i < c ; i++ )
  4002.                       r.unionWith((Rect)v.elementAt(i));
  4003.                   r.x = 0;
  4004.                   r.width = bounds.width;
  4005.                   r.y += imageRect.y;
  4006.                   r.height= imageRect.height;
  4007.                   addDirtyRect(r);
  4008.               }
  4009.           }
  4010.         }
  4011.     }
  4012.  
  4013.     boolean attributesChangingFormatting(Hashtable attr) {
  4014.         int i,c;
  4015.         Vector keysVector;
  4016.  
  4017.         if( attr != null ) {
  4018.             keysVector = attr.keysVector();
  4019.             for(i=0,c=keysVector.count() ; i < c ; i++ )
  4020.                 if(attributesChangingFormatting.indexOf(keysVector.elementAt(i)) != -1 ) {
  4021.                     return true;
  4022.                 }
  4023.         }
  4024.  
  4025.         return false;
  4026.     }
  4027.  
  4028.     void clearTypingAttributes() {
  4029.         if( _typingAttributes != null )
  4030.             _typingAttributes.clear();
  4031.     }
  4032.  
  4033.     void addAttributesForRangeWithoutNotification(Hashtable attributes, Range range) {
  4034.         Range  paragraphsRange;
  4035.         Vector runsVector;
  4036.         TextStyleRun run;
  4037.         TextParagraphFormat format;
  4038.         TextParagraph  paragraph;
  4039.         int i,c;
  4040.         Range runRange,paragraphRange;
  4041.         Range selectedRange = selectedRange();
  4042.         Range dirtyRange    = allocateRange();
  4043.         Vector toBeFormatted = new Vector();
  4044.         TextAttachment ti;
  4045.  
  4046.         if( attributes == null ) {
  4047.             recycleRange( dirtyRange);
  4048.             return;
  4049.         }
  4050.  
  4051.         if((ti = (TextAttachment) attributes.get(TEXT_ATTACHMENT_KEY)) != null )
  4052.             ti.setOwner(this);
  4053.  
  4054.         if( (format = (TextParagraphFormat)attributes.get(PARAGRAPH_FORMAT_KEY)) != null ) {
  4055.             paragraphsRange = paragraphsRangeForRange(range);
  4056.  
  4057.             for(i=paragraphsRange.index,c=paragraphsRange.index + paragraphsRange.length ;
  4058.                 i < c ; i++ ) {
  4059.                 paragraph = (TextParagraph)_paragraphVector.elementAt(i);
  4060.                 paragraph.setFormat(format);
  4061.                 toBeFormatted.addElementIfAbsent(paragraph);
  4062.                 dirtyRange.unionWith(paragraph.range());
  4063.             }
  4064.  
  4065.             if( attributes.count() == 1 ) {
  4066.                 for(i=0,c=toBeFormatted.count() ; i < c ; i++ )
  4067.                     formatParagraph((TextParagraph)toBeFormatted.elementAt(i));
  4068.                 dirtyRange( dirtyRange );
  4069.                 if( formattingEnabled() )
  4070.                     _selection.setRange(selectedRange.index,selectedRange.index+selectedRange.length);
  4071.                 recycleRange( dirtyRange );
  4072.                 recycleRange( paragraphsRange );
  4073.                 return;
  4074.             }
  4075.             recycleRange( paragraphsRange );
  4076.         }
  4077.  
  4078.         run = _runForIndex(range.index);
  4079.  
  4080.         if( run != null ) {
  4081.             runRange = run.range();
  4082.  
  4083.             if( range.equals(runRange)) {
  4084.                 run.appendAttributes(attributes);
  4085.                 dirtyRange.unionWith(run.range());
  4086.                 if(attributesChangingFormatting(attributes))
  4087.                     toBeFormatted.addElementIfAbsent( _paragraphForIndex(range.index));
  4088.                 for(i=0,c=toBeFormatted.count() ; i < c ; i++ ) {
  4089.                     paragraph = (TextParagraph) toBeFormatted.elementAt(i);
  4090.                     formatParagraph(paragraph);
  4091.                     dirtyRange.unionWith(paragraph.range());
  4092.                 }
  4093.                 dirtyRange( dirtyRange );
  4094.                 if( formattingEnabled() )
  4095.                     _selection.setRange(selectedRange.index,selectedRange.index+selectedRange.length);
  4096.                 recycleRange( dirtyRange );
  4097.                 recycleRange(runRange);
  4098.                 return;
  4099.             } else if( range.index >= runRange.index &&
  4100.                        (range.index + range.length) <= (runRange.index+runRange.length) &&
  4101.                        equalsAttributesHint(attributes,run.attributes())) {
  4102.                 recycleRange( dirtyRange );
  4103.                 recycleRange( runRange );
  4104.                 return;
  4105.             }
  4106.         }
  4107.  
  4108.         runsVector = createAndReturnRunsForRange(range);
  4109.  
  4110.         for(i=0,c=runsVector.count() ; i < c ; i++ ) {
  4111.             run = (TextStyleRun) runsVector.elementAt(i);
  4112.             run.appendAttributes(attributes);
  4113.             runRange = run.range();
  4114.             dirtyRange.unionWith(runRange);
  4115.             recycleRange(runRange);
  4116.         }
  4117.  
  4118.         if(attributesChangingFormatting(attributes)) {
  4119.             paragraphsRange = paragraphsRangeForRange(range);
  4120.             for(i=paragraphsRange.index, c = paragraphsRange.index+paragraphsRange.length ;
  4121.                 i < c ; i++ )
  4122.                 toBeFormatted.addElementIfAbsent(_paragraphVector.elementAt(i));
  4123.             recycleRange( paragraphsRange );
  4124.         }
  4125.  
  4126.         for(i = 0 , c = toBeFormatted.count() ; i < c ; i++ ) {
  4127.             Range pRange;
  4128.             paragraph = (TextParagraph) toBeFormatted.elementAt(i);
  4129.             //paragraph.collectEmptyRuns();
  4130.             formatParagraph(paragraph);
  4131.             pRange = paragraph.range();
  4132.             dirtyRange.unionWith( pRange );
  4133.             recycleRange( pRange );
  4134.         }
  4135.  
  4136.         dirtyRange( dirtyRange );
  4137.         if( formattingEnabled() )
  4138.             _selection.setRange(selectedRange.index,selectedRange.index+selectedRange.length);
  4139.         recycleRange( dirtyRange );
  4140.     }
  4141.  
  4142.     void validateHTMLParsingRules() {
  4143.         if( _htmlParsingRules == null ) {
  4144.             int i,c;
  4145.  
  4146.             /* Warning: If you add some markers, make sure in finishDecoding
  4147.                that you add the marker class in the html rules if
  4148.                it does not exist.
  4149.                */
  4150.             String supportedContainers[] = {
  4151.                 "BODY","H1","H2","H3","H4","H5","H6","B","STRONG",
  4152.                 "CENTER","EM","I","PRE","A","OL","UL","LI",
  4153.                 "ADDRESS","BLOCKQUOTE","DIR","MENU","TT","SAMP",
  4154.                 "CODE","KBD","VAR","CITE","DL","DT","DD","TITLE","P"};
  4155.  
  4156.             String supportedMarkers[]    = { "BR" , "HR", "IMG" };
  4157.  
  4158.             _htmlParsingRules = new HTMLParsingRules();
  4159.  
  4160.             for(i=0,c=supportedContainers.length ; i < c ; i++ )
  4161.                 _htmlParsingRules.setClassNameForMarker(
  4162.                 "netscape.application.TextViewHTMLContainerImp", supportedContainers[i]);
  4163.  
  4164.             for(i=0,c=supportedMarkers.length ; i < c ; i++ )
  4165.                 _htmlParsingRules.setClassNameForMarker(
  4166.                 "netscape.application.TextViewHTMLMarkerImp", supportedMarkers[i]);
  4167.  
  4168.             _htmlParsingRules.setStringClassName("netscape.application.TextViewHTMLString");
  4169.         }
  4170.     }
  4171.  
  4172.     void disableAttachmentNotification() {
  4173.       notifyAttachmentDisabled++;
  4174.     }
  4175.  
  4176.     void enableAttachmentNotification() {
  4177.       notifyAttachmentDisabled--;
  4178.       if(notifyAttachmentDisabled<0)
  4179.         notifyAttachmentDisabled = 0;
  4180.       if(notifyAttachmentDisabled == 0 && invalidAttachmentRange!=null) {
  4181.         notifyAttachmentsForRange(invalidAttachmentRange,true);
  4182.         invalidAttachmentRange = null;
  4183.       }
  4184.     }
  4185.  
  4186.    void _notifyAttachmentsForRange(Range aRange,boolean added) {
  4187.         int paragraphIndex = _paragraphIndexForIndex(aRange.index);
  4188.         int runIndex;
  4189.         TextParagraph p;
  4190.         TextStyleRun run;
  4191.         int index = aRange.index;
  4192.         int lastIndex = aRange.index + aRange.length;
  4193.         Hashtable attributes;
  4194.         TextAttachment attachment;
  4195.  
  4196.         if(paragraphIndex == -1)
  4197.           return;
  4198.         p = (TextParagraph) _paragraphVector.elementAt(paragraphIndex);
  4199.         runIndex = p.runIndexForCharPosition(index);
  4200.         if(runIndex == -1)
  4201.           return;
  4202.  
  4203.         run = (TextStyleRun) p._runVector.elementAt(runIndex);
  4204.         index = run.rangeIndex();
  4205.         while(index < lastIndex) {
  4206.             attributes = run.attributes();
  4207.             if(attributes != null &&
  4208.                (attachment = (TextAttachment) attributes.get(TEXT_ATTACHMENT_KEY)) != null) {
  4209.               if(added) {
  4210.                 TextPositionInfo info = run._paragraph._infoForPosition(run.rangeIndex());
  4211.                 if( info != null ) {
  4212.                   Rect r;
  4213.  
  4214.                   info.representCharacterAfterEndOfLine();
  4215.                   r = run.textAttachmentBoundsForOrigin(info._x,info._y,
  4216.                                                         run._paragraph._baselines[info._lineNumber]);
  4217.                   attachment._willShowWithBounds(r);
  4218.                 }
  4219.               } else
  4220.                   attachment._willHide();
  4221.             }
  4222.  
  4223.             index += run.charCount();
  4224.             runIndex++;
  4225.             if(runIndex < (p._runVector.count()))
  4226.               run = (TextStyleRun) p._runVector.elementAt(runIndex);
  4227.             else {
  4228.               paragraphIndex++;
  4229.               index++; /** Return char **/
  4230.               if(paragraphIndex < (_paragraphVector.count())) {
  4231.                 p = (TextParagraph) _paragraphVector.elementAt(paragraphIndex);
  4232.                 runIndex = 0;
  4233.                 if(p._runVector.count() > 0)
  4234.                   run = (TextStyleRun) p._runVector.elementAt(runIndex);
  4235.                 else
  4236.                   break;
  4237.               } else
  4238.                 break;
  4239.             }
  4240.         }
  4241.     }
  4242.  
  4243.     void notifyAttachmentsForRange(Range aRange,boolean added) {
  4244.       if(added == false) {
  4245.         _notifyAttachmentsForRange(aRange,false);
  4246.       } else {
  4247.         if(notifyAttachmentDisabled > 0) {
  4248.           if(invalidAttachmentRange !=  null)
  4249.             invalidAttachmentRange.unionWith(aRange);
  4250.           else
  4251.             invalidAttachmentRange = new Range(aRange);
  4252.         } else {
  4253.           _notifyAttachmentsForRange(aRange,true);
  4254.         }
  4255.       }
  4256.     }
  4257.  
  4258.     boolean isLeftHalfOfCharacter(int x,int y) {
  4259.         TextPositionInfo info,absoluteInfo;
  4260.  
  4261.         info = positionForPoint(x,y,false);
  4262.         absoluteInfo = positionForPoint(x,y,true);
  4263.         if(info == null || absoluteInfo == null)
  4264.             return true;
  4265.  
  4266.         if(info._absPosition == absoluteInfo._absPosition)
  4267.             return true;
  4268.         else
  4269.             return false;
  4270.     }
  4271.  
  4272.  
  4273.  
  4274.  
  4275.     static Range allocateRange() {
  4276.         return allocateRange(Range.nullRange().index,Range.nullRange().length);
  4277.     }
  4278.  
  4279.     static Range allocateRange(Range template) {
  4280.         return allocateRange(template.index,template.length);
  4281.     }
  4282.  
  4283.     static Range allocateRange(int index,int length) {
  4284.         Range result = (Range) rangePool.allocateObject();
  4285.         result.index = index;
  4286.         result.length = length;
  4287.         return result;
  4288.     }
  4289.  
  4290.     static void recycleRange(Range aRange) {
  4291.         rangePool.recycleObject(aRange);
  4292.     }
  4293.  
  4294.     /** Copys the current selection.
  4295.       *
  4296.       */
  4297.     public void copy() {
  4298.         Application.setClipboardText(stringForRange(selectedRange()));
  4299.     }
  4300.  
  4301.     /** Cuts the current selection.
  4302.       *
  4303.       */
  4304.     public void cut() {
  4305.         if (isEditable()) {
  4306.             Range range = selectedRange();
  4307.  
  4308.             Application.setClipboardText(stringForRange(range));
  4309.             replaceRangeWithString(range, "");
  4310.             selectRange(new Range(range.index(), 0));
  4311.         }
  4312.     }
  4313.  
  4314.     /** Replaces the current selection.
  4315.       *
  4316.       */
  4317.     public void paste() {
  4318.         if (isEditable()) {
  4319.             Range range = selectedRange();
  4320.             String text = Application.clipboardText();
  4321.  
  4322.             if(range == null || range.index == -1 || text == null)
  4323.                 return;
  4324.  
  4325.             _selection.disableInsertionPoint();
  4326.             replaceRangeWithString(range, text);
  4327.  
  4328.             range = new Range(range.index() + text.length(), 0);
  4329.             selectRange(range);
  4330.             scrollRangeToVisible(range);
  4331.             _selection.enableInsertionPoint();
  4332.         }
  4333.     }
  4334.  
  4335.     /** Implementation of the FormElement interface
  4336.       *
  4337.       */
  4338.     public String formElementText() {
  4339.         return string();
  4340.     }
  4341.  
  4342. }
  4343.